package com.uroad.jilinpda.util;

import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;


import com.android.recharge.DESJNILib;
import com.android.recharge.ObuInterface;
import com.android.util.MyUtil;
import com.genvict.core.GvDevOptConstant;
import com.genvict.core.entity.CpcEntryExitInfo;
import com.genvict.core.entity.CpcSysInfo;
import com.genvict.core.entity.ObuVehInfo;
import com.genvict.core.entity.TransInfo;
import com.genvict.core.utils.GvConvertUtils;
import com.genvict.core.utils.GvStringUtils;
import com.genvict.core.utils.GvTimeUtils;
import com.genvict.etc.CardConsumeCalcMac1Params;
import com.genvict.etc.DebitForPurchaseInitParams;
import com.genvict.etc.DebitForPurchaseParams;
import com.genvict.etc.UpdateFileParams;
import com.genvict.handset.SdkConstant;
import com.uroad.jilinpda.common.GlobalData;
import com.uroad.jilinpda.modle.CPCEF02EF04InfoMDL;
import com.uroad.jilinpda.modle.CpcEntryExitInfoMDL;
import com.uroad.jilinpda.modle.CpcEntryMDL;
import com.uroad.jilinpda.modle.CpcSysInfoMDL;
import com.uroad.jilinpda.modle.OBUEF04MDL;
import com.uroad.lib.util.log.LogUtil;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.TimeZone;

import etc.obu.data.CardInformation;
import etc.obu.data.CardOwner;
import etc.obu.data.ObuSystemInformation;
import etc.obu.data.ReturnStatus;


/**
 * @author jayz
 */
public class SdkHelper {
    public static final String LOG_FLAG = "GENVICT";
    public static final String DEV_MODEL_M900 = "TC4_DVT";
    public static final String DEV_MODEL_M700 = "M700";
    public static final String DEV_MODEL_P700 = "JDN-AL00";
    public static final String DEV_MODEL_P700_M5 = "JDN2-AL00";

    private static volatile SdkHelper instance;
    private ObuKeepAlive keepAlive;
    private boolean isNeedAlive = false;//是否需要保活
    private ObuInterface sdk;
    private Context context;
    private int devType = SdkConstant.HANDSET_TYPE_M700;
    public static int cardSlot;
    public String quyudaima;
    public String kahao;
    public CpcSysInfoMDL cpcSysInfo;

    protected static String fileName;
    protected static String fileBasePath = Environment.getExternalStorageDirectory().getPath()+"/lnM700log/";
    protected static String filePath;
    protected SimpleDateFormat logFormat = new SimpleDateFormat("_yyyy_MM_dd");
    protected SimpleDateFormat logFormats = new SimpleDateFormat("HH:mm:ss");
    protected static String channelType;
    protected static long difference;
    protected static long lastTime;
    private String tollName;
    //天线是否打开 true 打开  false 未打开
    private static boolean isOpenDsrc = false;

    /**
     * 获取sdk单例
     * @param context context
     * @return
     */
    public static SdkHelper getInstance(Context context) {
        if (instance == null) {
            synchronized (SdkHelper.class) {
                if (instance == null) {
                    instance = new SdkHelper(context);
                }
            }
        }
        return instance;
    }

    /**
     * 重置sdk
     * @param context
     * @return
     */
    public static SdkHelper resertInstance(Context context) {
        if (instance != null) {
            instance.closeDsrc();
            instance = null;
            getInstance(context);
        }else{
            getInstance(context);
        }
        return instance;
    }

    private SdkHelper(Context context) {
        this.context = context.getApplicationContext();
        if ( Build.MODEL.equals(DEV_MODEL_M700) ){
            devType = SdkConstant.HANDSET_TYPE_M700;
        } else if (Build.MODEL.equals(DEV_MODEL_P700) || Build.MODEL.equals(DEV_MODEL_P700_M5)) {
            devType = SdkConstant.HANDSET_TYPE_P700;
        }
        sdk = new ObuInterface(context, devType);
        cardSlot = (int) SPUtils.get(context, GlobalData.spCardSlot, 3);
        keepAlive = ObuKeepAlive.getInstance();
        initLogFile();
    }

    /**
     * 初始化日志时间
     */
    protected void initLogFile() {
        difference = (long) SPUtils.get(context, GlobalData.sp_difference, 0L);
        tollName = (String) SPUtils.get(context, GlobalData.spTollName, "");
        logFormat.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        logFormats.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        fileName = "lnm700log" + tollName + logFormat.format(System.currentTimeMillis() + difference) +"_"+Build.SERIAL+ ".txt";
        filePath = fileBasePath + fileName;
    }

    public ObuInterface getSdk() {
        return sdk;
    }

    public int getDevType() {
        return devType;
    }

    public boolean isNeedAlive() {
        return isNeedAlive;
    }

    public void setNeedAlive(boolean needAlive) {
        isNeedAlive = needAlive;
    }

    /**
     * 打开天线设备
     * @return GvApiResult 0成功，非0失败
     */
    public GvApiResult openDsrc() {
        LogUtils.i("打开天线");
        if (isOpenDsrc) {
            return GvApiResult.SUCCESS;
        }
        writeLog("打开天线");
        int optResult = -1;
        switch (devType) {
            case SdkConstant.HANDSET_TYPE_M700:
                optResult = sdk.dsrcOpen();
                break;
            case SdkConstant.HANDSET_TYPE_P700:
                optResult = sdk.dsrcWakeUp();
                break;
            default:
        }

        if (optResult >= 0) {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            isOpenDsrc = true;
            LogUtils.i("打开天线成功");
            return GvApiResult.SUCCESS;
        } else if(optResult == ReturnStatus.USB_DISCONNECTED) {
            Log.e(LOG_FLAG, "Open dsrc failed, sdk return: " + optResult);
            return GvApiResult.DSRC_USB_DISCONNECTED;
        } else if (optResult == ReturnStatus.USB_PERMISSION_DENIED) {
            Log.e(LOG_FLAG, "Open dsrc failed, sdk return: " + optResult);
            return GvApiResult.DSRC_USB_PERMISSION_DENIED;
        } else {
            Log.e(LOG_FLAG, "Open dsrc failed, sdk return: " + optResult);
            return GvApiResult.OPEN_DSRC_FAILED;
        }
    }

    /**
     * 关闭天线
     * @return GvApiResult 0成功，非0失败
     */
    public GvApiResult closeDsrc() {
        LogUtils.i("关闭天线");
        if (!isOpenDsrc) {
            return GvApiResult.SUCCESS;
        }
        writeLog("关闭天线");
        int optResult = -1;
        switch (devType) {
            case SdkConstant.HANDSET_TYPE_M700:
                sdk.dsrcClose();
                optResult = 0;
                break;
            case SdkConstant.HANDSET_TYPE_P700:
                optResult = sdk.dsrcSleep();
                break;
            default:
        }

        if (optResult >= 0) {
            isOpenDsrc = false;
            LogUtils.i("关闭天线成功");
            return GvApiResult.SUCCESS;
        } else if(optResult == ReturnStatus.USB_DISCONNECTED) {
            Log.e(LOG_FLAG, "Close dsrc failed, sdk return: " + optResult);
            return GvApiResult.DSRC_USB_DISCONNECTED;
        } else if (optResult == ReturnStatus.USB_PERMISSION_DENIED) {
            Log.e(LOG_FLAG, "Close dsrc failed, sdk return: " + optResult);
            return GvApiResult.DSRC_USB_PERMISSION_DENIED;
        } else {
            Log.e(LOG_FLAG, "Close dsrc failed, sdk return: " + optResult);
            return GvApiResult.CLOSE_DSRC_FAILED;
        }
    }

    /**
     * 搜索标签
     * @param obuSysInfo 标签系统信息
     * @param cardInfo 卡片信息
     * @return GvApiResult : 0成功，非0失败
     */
    public GvApiResult findObu(ObuSystemInformation obuSysInfo, CardInformation cardInfo) {
        int optResult = sdk.findObu(obuSysInfo, cardInfo);
        if(optResult != 0){
            Log.e(LOG_FLAG,"没有搜索到OBU！");
            return GvApiResult.OBU_NO_FIND;
        }
        Log.i(LOG_FLAG,"Find obu: " + obuSysInfo.getSerialNumber());

        return GvApiResult.SUCCESS;
    }

    /**
     * 搜索OBU或CPC
     * @param obuSysInfo obu系统信息
     * @param cardInfo 卡片0015信息
     * @param cpcSysInfo CPC卡系统信息
     * @param entryExitInfo 出入口信息
     * @param vstType 返回的VST类型：0：OBU，1：CPC
     * @return 0成功，非0失败
     */
    public GvApiResult findObuOrCpc(ObuSystemInformation obuSysInfo, CardInformation cardInfo, CpcSysInfo cpcSysInfo, CpcEntryExitInfo entryExitInfo, int[] vstType) {
        int optResult = sdk.findObu(obuSysInfo, cardInfo, cpcSysInfo, entryExitInfo, vstType);
        if(optResult != 0){
            Log.e(LOG_FLAG,"没有搜索到OBU或CPC卡！");
            return GvApiResult.OBU_OR_CPC_NO_FIND;
        }
        Log.i(LOG_FLAG, "搜索到" + (vstType[0] == 0? "OBU: " + obuSysInfo.getSerialNumber() : "CPC: " + cardInfo.getCardId()));
        return GvApiResult.SUCCESS;
    }

    /**
     * 判断cos指令执行状态码
     * @author ZengXiaoJiang
     * @param cosNum cos指令返回的条数
     * @param cosRsps 输出cos指令返回的数据
     * @return GvApiResult 0成功，非0失败
     */
    private GvApiResult judgeCosRspStatus(int cosNum, String[] cosRsps) {
        if (null == cosRsps) {
            return GvApiResult.API_PARAMS_ERROR;
        }

        try {
            for (int i = 0; i < cosNum; i++) {
                Log.i(LOG_FLAG, String.format("judgeCosRspStatus, cosRsps[%d]: %s", i, cosRsps[i]));
                if (cosRsps[i].length() < 4) {
                    return GvApiResult.DSRC_EXEC_COS_FAILED;
                }

                String cosRspStatus = cosRsps[i].substring(cosRsps[i].length() - 4, cosRsps[i].length());
                if (!cosRspStatus.equals("9000")) {
                    Log.i(LOG_FLAG,"judgeCosRspStatus, cos response status code: " + cosRspStatus);
                    int cosStatusIntValue = Integer.parseInt(cosRspStatus, 16);
                    for (GvApiResult result : GvApiResult.values()) {
                        if (cosStatusIntValue == result.getCode()) {
                            return result;
                        }
                    }


                    if (cosStatusIntValue >= GvApiResult.COS_SW_6100.getCode()
                            && cosStatusIntValue < GvApiResult.COS_SW_6200.getCode()) {
                        //61xx 正常 需发GET RESPONSE命令，这里统一返回0x6100
                        return GvApiResult.COS_SW_6100;
                    }

                    if (cosStatusIntValue >= GvApiResult.COS_SW_6C00.getCode()
                            && cosStatusIntValue < GvApiResult.COS_SW_6D00.getCode()) {
                        //6Cxx Le长度错误，实际长度是xx，这里统一返回0x6C00
                        return GvApiResult.COS_SW_6C00;
                    }

                    if (cosStatusIntValue >= GvApiResult.COS_SW_63C0.getCode()
                            && cosStatusIntValue <= 0x63CF) {
                        //63Cx 警告 校验失败（x－允许重试次数)，这里统一返回0x63C0
                        return GvApiResult.COS_SW_63C0;
                    }
                    return GvApiResult.valueOf(cosStatusIntValue);
                }
            }
            return GvApiResult.SUCCESS;

        } catch (Exception e) {
            Log.e(LOG_FLAG,"Judge cos response status catch exception: " + e.getMessage());
            writeLog("指令结果异常："+cosRsps.toString()+"\n"+e.getMessage());
            return GvApiResult.JUDGE_COS_RSP_STATUS_CATCH_EXCEPTION;
        }
    }

    /**
     * cos指令通道
     * @param channelType 通道类型，DSRC模块：GvDevOptConstant.CHANNEL_TYPE_DSRC， OBU模块：GvDevOptConstant.CHANNEL_TYPE_OBU
     * @param channelID DSRC通道：0-ICC通道，1-SAM1，2-SAM2, 3-SAM3，4-SAM4；OBU通道：1-ICC，2-ESAM
     * @param cosNum cos指令条数
     * @param cosCmds 存放cos指令的String数组，改参数同时输出执行cos指令后返回的String数组
     * @return GvApiResult : 0成功，非0失败
     * @Throw Exception
     */
    public GvApiResult cosChannel(int channelType, int channelID, int cosNum, String[] cosCmds) throws Exception{
        if (keepAlive.isKeepAlive()) {
            keepAlive.setTaskStatus(1);
            int tryTimes = 40;
            while(true) {
                tryTimes--;
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (keepAlive.getTaskStatus() == 2) {
                    break;
                }
                if (tryTimes < 1) {
                    break;
                }
            }
            keepAlive.stop();
            LogUtils.i("CMD保活结束");
        }
//        RandomAccessFile raf = null;
        try {
            if (null == cosCmds) {
                return GvApiResult.API_PARAMS_ERROR;
            }
            if (channelType == GvDevOptConstant.CHANNEL_TYPE_DSRC) {
                if (channelID > 0) {
                    this.channelType = "PSAM";
                }else {
                    this.channelType = "ICC";
                }
            } else if (channelID == 1) {
                this.channelType = "ICC";
            }else{
                this.channelType = "ESAM";
            }
            //创建文件夹及文件
//            File file = FileUtils.makeFilePath(fileBasePath, fileName);
//            raf = new RandomAccessFile(file, "rwd");
//            raf.seek(file.length());//跳转到文件末尾

            int ret = -1, execCosCmdTryTimes = 2;
            Log.i(LOG_FLAG, "[EtcDevOpt.cosChannel] channelType = " + channelType + " ChannelID = " + channelID);
            for (int i = 0; i < execCosCmdTryTimes; i++) {
                for (int j = 0; j < cosNum; j++) {
                    String cosCmd = String.format("[%d]: %s", j, cosCmds[j]);
                    Log.i(LOG_FLAG, "发送指令"+cosCmd);
                    long nowTime = System.currentTimeMillis()+difference;
//                    raf.write((logFormats.format(nowTime) + "-" + this.channelType + "-发送：" + cosCmd +" 耗时"+(nowTime - lastTime)+ "\r\n").getBytes());
                    lastTime = nowTime;
                }

                switch (channelType) {
                    //dsrc模块通道
                    case GvDevOptConstant.CHANNEL_TYPE_DSRC:
                        ret = sdk.dsrcCosCommand((byte) channelID, cosNum, cosCmds);
                        break;
                    //obu通道
                    case GvDevOptConstant.CHANNEL_TYPE_OBU:
                        ret = sdk.obuCosCommand((byte) channelID, cosNum, cosCmds);
                        break;

                    default:
                        break;
                }
                long nowTime = System.currentTimeMillis()+difference;
//                raf.write((logFormats.format(nowTime) + "-" + this.channelType + "-执行：" + ret +" 耗时"+(nowTime - lastTime)+ "\r\n").getBytes());
                lastTime = nowTime;
                if (ret == 0) {
                    //执行成功
                    Log.i(LOG_FLAG, "指令返回"+cosCmds[0]);
//                    raf.write((logFormats.format(nowTime) + "-" + this.channelType + "-返回：" + cosCmds[0] +" 耗时"+(nowTime - lastTime)+ "\r\n").getBytes());
                    lastTime = nowTime;
                    break;
                }else{
                    //执行失败
//                    raf.write((logFormats.format(nowTime) + "-" + this.channelType + "-执行失败：" + ret + " 耗时" + (nowTime - lastTime) + "\r\n").getBytes());
                    lastTime = nowTime;
                }
            }
//            raf.close();
            if (isNeedAlive) {
                keepAlive.start();
            }
            if (ret == 0) {
                return this.judgeCosRspStatus(cosNum, cosCmds);
            } else {
                Log.e(LOG_FLAG, "Send cos to dsrc failed, sdk return: " + ret);
                return GvApiResult.SEND_COS_TO_DSRC_FAILED;
            }
        } catch (Exception e) {
            /*if (raf != null) {
                raf.close();
            }*/
            throw new Exception();
        }
    }
    /**
     * cos指令保活通道 与cos
     * @param channelType 通道类型，DSRC模块：GvDevOptConstant.CHANNEL_TYPE_DSRC， OBU模块：GvDevOptConstant.CHANNEL_TYPE_OBU
     * @param channelID DSRC通道：0-ICC通道，1-SAM1，2-SAM2, 3-SAM3，4-SAM4；OBU通道：1-ICC，2-ESAM
     * @param cosNum cos指令条数
     * @param cosCmds 存放cos指令的String数组，改参数同时输出执行cos指令后返回的String数组
     * @return GvApiResult : 0成功，非0失败
     * @Throw Exception
     */
    public GvApiResult cosChannelAlive(int channelType, int channelID, int cosNum, String[] cosCmds) throws Exception{
        if (!isNeedAlive) {
            keepAlive.stop();
            return GvApiResult.SUCCESS;
        }
        RandomAccessFile raf = null;
        try {
            if (null == cosCmds) {
                return GvApiResult.API_PARAMS_ERROR;
            }
            if (channelType == GvDevOptConstant.CHANNEL_TYPE_DSRC) {
                if (channelID > 0) {
                    this.channelType = "PSAM";
                }else {
                    this.channelType = "ICC";
                }
            } else if (channelID == 1) {
                this.channelType = "ICC";
            }else{
                this.channelType = "ESAM";
            }
            //创建文件夹及文件
//            File file = FileUtils.makeFilePath(fileBasePath, fileName);
//            raf = new RandomAccessFile(file, "rwd");
//            raf.seek(file.length());//跳转到文件末尾

            int ret = -1, execCosCmdTryTimes = 2;
            Log.i(LOG_FLAG, "[EtcDevOpt.cosChannel] channelType = " + channelType + " ChannelID = " + channelID);
            for (int i = 0; i < execCosCmdTryTimes; i++) {
                for (int j = 0; j < cosNum; j++) {
                    String cosCmd = String.format("[%d]: %s", j, cosCmds[j]);
                    Log.i(LOG_FLAG, "发送指令"+cosCmd);
                    long nowTime = System.currentTimeMillis()+difference;
//                    raf.write((logFormats.format(nowTime) + "-" + this.channelType + "-发送：" + cosCmd +" 耗时"+(nowTime - lastTime)+ "\r\n").getBytes());
                    lastTime = nowTime;
                }

                switch (channelType) {
                    //dsrc模块通道
                    case GvDevOptConstant.CHANNEL_TYPE_DSRC:
                        ret = sdk.dsrcCosCommand((byte) channelID, cosNum, cosCmds);
                        break;
                    //obu通道
                    case GvDevOptConstant.CHANNEL_TYPE_OBU:
                        ret = sdk.obuCosCommand((byte) channelID, cosNum, cosCmds);
                        break;

                    default:
                        break;
                }
                long nowTime = System.currentTimeMillis()+difference;
//                raf.write((logFormats.format(nowTime) + "-" + this.channelType + "-执行：" + ret +" 耗时"+(nowTime - lastTime)+ "\r\n").getBytes());
                lastTime = nowTime;
                if (ret == 0) {
                    //执行成功
                    Log.i(LOG_FLAG, "指令返回"+cosCmds[0]);
//                    raf.write((logFormats.format(nowTime) + "-" + this.channelType + "-返回：" + cosCmds[0] +" 耗时"+(nowTime - lastTime)+ "\r\n").getBytes());
                    lastTime = nowTime;
                    break;
                }else{
                    //执行失败
//                    raf.write((logFormats.format(nowTime) + "-" + this.channelType + "-执行失败：" + ret + " 耗时" + (nowTime - lastTime) + "\r\n").getBytes());
                    lastTime = nowTime;
                }
            }
//            raf.close();
            return GvApiResult.SUCCESS;
//            if (ret == 0) {
//                return this.judgeCosRspStatus(cosNum, cosCmds);
//            } else {
//                Log.e(LOG_FLAG, "Send cos to dsrc failed, sdk return: " + ret);
//                return GvApiResult.SEND_COS_TO_DSRC_FAILED;
//            }
        } catch (Exception e) {
            throw e;
        }finally {
            /*if (raf != null) {
                raf.close();
            }*/
        }
    }

    /**
     * 获取随机数
     * @param channelType 通道类型，DSRC模块：GvDevOptConstant.CHANNEL_TYPE_DSRC， OBU模块：GvDevOptConstant.CHANNEL_TYPE_OBU
     * @param channelID DSRC通道：0-ICC通道，1-SAM1，2-SAM2, 3-SAM3，4-SAM4；OBU通道：1-ICC，2-ESAM
     * @param challengeLen : 随机数长度，4字节或8字节
     * @param outData : 输出随机数
     * @return GvApiResult : 0成功，非0失败
     */
    public GvApiResult getChallenge(int channelType, int channelID, byte challengeLen, String[] outData) throws Exception{
        if (outData == null) {
            return GvApiResult.API_PARAMS_ERROR;
        }

        if(challengeLen != 4 && challengeLen != 8){
            challengeLen = 4;
        }

        //make apdu 1, get challenge, 00840000+challengeLen
        String[] cosCmds = new String[1];
        int cosNums = 0;
        cosCmds[cosNums++] = String.format("00840000%02X", challengeLen);
        GvApiResult result = cosChannel(channelType, channelID, cosNums, cosCmds);
        if (result == GvApiResult.SUCCESS) {
            int rspCosIndex = cosNums-1;
            outData[0] = cosCmds[rspCosIndex].substring(0, cosCmds[rspCosIndex].length()-4);
        }

        return result;
    }

    /**
     * 进入目录
     * @param channelType 通道类型，DSRC模块：EtcDevOptConstant.CHANNEL_TYPE_DSRC， OBU模块：EtcDevOptConstant.CHANNEL_TYPE_OBU
     * @param channelID DSRC通道：0-ICC通道，1-SAM1，2-SAM2, 3-SAM3，4-SAM4；OBU通道：1-ICC，2-ESAM
     * @param targetDir 待进入的目标目录
     * @return GvApiResult : 0成功，非0失败
     */
    public GvApiResult enterDir(int channelType, int channelID, String targetDir) throws Exception {
        String[] cosCmds = new String[2];
        int cosNums = 0;

        //指令1：进入3F00 目录 00A40000023F00
        cosCmds[cosNums++] = "00A40000023F00";

        String MAIN_DIR = "3F00";
        if (!targetDir.equals(MAIN_DIR)) {
            //指令2，进入目录
            cosCmds[cosNums++] = "00A4000002" + targetDir;
        }
        GvApiResult result = this.cosChannel(channelType, channelID, cosNums, cosCmds);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "Enter targetDir "+ targetDir +" return failed. error code: " + result.getCode());
        }

        return result;
    }

    /**
     * 读取IC卡文件信息，包括0015、0019、0002文件
     * @param channelType 通道类型，DSRC模块：GvDevOptConstant.CHANNEL_TYPE_DSRC， OBU模块：GvDevOptConstant.CHANNEL_TYPE_OBU
     * @param channelID DSRC通道：0-ICC通道，1-SAM1，2-SAM2, 3-SAM3，4-SAM4；OBU通道：1-ICC，2-ESAM
     * @param file0015 : 0015文件
     * @param file0019 : 0019文件
     * @param file0002 : 0002文件
     * @return GvApiResult : 0成功，非0失败
     */
    public GvApiResult readICC(int channelType, int channelID, String[] file0015, String[] file0019, String[] file0002) throws Exception {

        if(file0015 == null || file0019 == null || file0002 == null){
            return GvApiResult.API_PARAMS_ERROR;
        }

        int readICC0015Length = 50, readICC0019Length = 43, readICC0006Length = 4;

        String[] cosCmds = new String[3];
        int cosNums = 0;
        //指令1：00B201CC2B 读取IC Card 0015文件43个字节
        cosCmds[cosNums++] = String.format("00B09500%02X", (byte)readICC0015Length);
        //指令2：00B201CC2B 读取IC Card 0019文件43个字节
        cosCmds[cosNums++] = String.format("00B201CC%02X", (byte)readICC0019Length);
        //指令3，805C000204 读0002钱包文件
        cosCmds[cosNums++] = String.format("805C0002%02X", (byte)readICC0006Length);
        //执行指令
        GvApiResult result = this.cosChannel(channelType, channelID, cosNums, cosCmds);
        if (result == GvApiResult.SUCCESS) {
            file0015[0] = cosCmds[0].substring(0, cosCmds[0].length()-4);
            file0019[0] = cosCmds[1].substring(0, cosCmds[1].length()-4);
            file0002[0] = cosCmds[2].substring(0, cosCmds[2].length()-4);
        }
        return result;
    }

    /**
     * 读取IC卡余额（0002文件）
     * @param channelType 通道类型，DSRC模块：GvDevOptConstant.CHANNEL_TYPE_DSRC， OBU模块：GvDevOptConstant.CHANNEL_TYPE_OBU
     * @param channelID DSRC通道：0-ICC通道，1-SAM1，2-SAM2, 3-SAM3，4-SAM4；OBU通道：1-ICC，2-ESAM
     * @param balance 读取的余额
     * @return GvApiResult: 0成功，非0失败
     */
    public GvApiResult readICCBalance(int channelType, int channelID, int[] balance) throws Exception {
        if (balance == null) {
            return GvApiResult.API_PARAMS_ERROR;
        }

        String[] cosCmds = new String[1];
        int cosNums = 0;
        //指令1：805C000204 读0002钱包文件
        cosCmds[cosNums++] = String.format("805C0002%02X", (byte)4);
        //执行指令
        GvApiResult result = this.cosChannel(channelType, channelID, cosNums, cosCmds);
        if (result != GvApiResult.SUCCESS) {
            Log.i(LOG_FLAG, "Read balance(file 0002) failed, error code: " + result.getCode());
            return result;
        }
        int rspCosIndex = cosNums-1;
        String newBalanceStr = cosCmds[rspCosIndex].substring(0, cosCmds[rspCosIndex].length()-4);
        balance[0] = GvConvertUtils.bytesToBigInt(GvConvertUtils.hexStringToBytes(newBalanceStr));

        return GvApiResult.SUCCESS;
    }

    /**
     * 读取卡片0019记录
     * @param channelType 通道类型，DSRC模块：GvDevOptConstant.CHANNEL_TYPE_DSRC， OBU模块：GvDevOptConstant.CHANNEL_TYPE_OBU
     * @param channelID DSRC通道：0-ICC通道，1-SAM1，2-SAM2, 3-SAM3，4-SAM4；OBU通道：1-ICC，2-ESAM
     * @param file0019 返回的0019记录
     * @return GvApiResult : 0成功，非0失败
     * @throws Exception
     */
    public GvApiResult readCardFile0019(int channelType, int channelID, String[] file0019) throws Exception {
        if(file0019 == null ){
            return GvApiResult.API_PARAMS_ERROR;
        }

        int readICC0019Length = 43;
        String[] cosCmds = new String[1];
        int cosNums = 0;
        //指令1：进入卡片1001
        cosCmds[cosNums++] = "00A40000021001";
        GvApiResult result = this.cosChannel(channelType, channelID, cosNums, cosCmds);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        cosNums = 0;
        //指令2：00B201CC2B 读取IC Card 0019文件记录1的43个字节
        cosCmds[cosNums++] = String.format("00B201CC%02X", readICC0019Length);
        result = this.cosChannel(channelType, channelID, cosNums, cosCmds);
        if (result == GvApiResult.SUCCESS) {
            int rspCosIndex = cosNums-1;
            file0019[0] = cosCmds[rspCosIndex].substring(0, cosCmds[rspCosIndex].length()-4);
        }

        return result;
    }

    /**
     * 写0019记录文件
     * @param channelType 通道类型，DSRC模块：GvDevOptConstant.CHANNEL_TYPE_DSRC， OBU模块：GvDevOptConstant.CHANNEL_TYPE_OBU
     * @param channelID DSRC通道：0-ICC通道，1-SAM1，2-SAM2, 3-SAM3，4-SAM4；OBU通道：1-ICC，2-ESAM
     * @param reqData 待写的0019记录（43字节）
     * @return GvApiResult : 0成功，非0失败
     * @throws Exception
     */
    public GvApiResult writeCardFile0019(int channelType, int channelID, String reqData) throws Exception {
        if(reqData == null ){
            return GvApiResult.API_PARAMS_ERROR;
        }

        String[] cosCmds = new String[1];
        int cosNums = 0;
        //指令1：进入卡片1001
        cosCmds[cosNums++] = "00A40000021001";
        GvApiResult result = this.cosChannel(channelType, channelID, cosNums, cosCmds);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        cosNums = 0;
        //指令1：复合写0019：80DCAAC8+len+data ,AA2900固定3字节
        cosCmds[cosNums++] = String.format("80DCAAC82BAA2900%s", reqData.substring(6));
        //执行指令
        return this.cosChannel(channelType, channelID, cosNums, cosCmds);
    }

    /**
     * 读取EF04路径文件
     * @param channelType 通道类型
     * @param channelID 通道号
     * @param fileSFI 文件标识
     * @param offset 偏移量
     * @param readLen 读取长度
     * @param rspData 读取的数据
     * @return GvApiResult : 0成功，非0失败
     * @throws Exception
     */
    public GvApiResult readPathFile(int channelType, int channelID, byte fileSFI, byte offset, byte readLen, String[] rspData) throws Exception {
        //read file
        String[] cosCmds = new String[1];
        int cosNums = 0;
        cosCmds[cosNums++] = String.format("00B0%02X%02X%02X", fileSFI, offset, readLen);
        GvApiResult result = this.cosChannel(channelType, channelID, cosNums, cosCmds);
        if (result == GvApiResult.SUCCESS) {
            rspData[0] = cosCmds[0].substring(0, cosCmds[0].length()-4);
        }
        return result;
    }

    /**
     * 更新路径文件
     * @param channelType 通道类型
     * @param channelID 通道号
     * @param fileSFI 文件标识
     * @param offset 偏移量
     * @param writeLen 写数据长度
     * @param writeData 写入的数据
     * @return GvApiResult : 0成功，非0失败
     * @throws Exception
     */
    public GvApiResult updatePathFile(int channelType, int channelID, byte fileSFI, byte offset, byte writeLen, String writeData) throws Exception {
        //make write file instruct: 00 D6 8B 00 01 11
        String instData = String.format("00D6%02X%02X%02X%s", fileSFI, offset, writeLen, writeData);

        //write file
        String[] cosCmds = new String[1];
        int cosNums = 0;
        cosCmds[cosNums++] = instData;
        return this.cosChannel(channelType, channelID, cosNums, cosCmds);
    }

    /**
     * 填充MAC计算需要的数据：随机数+写指令+数据补80，然后补全到8的倍数
     * @param inData : 输入数据
     * @param outData : 输出数据
     * @return GvApiResult : 0成功，非0失败
     */
    public GvApiResult makeMacInput(String inData, String[] outData){
        if (TextUtils.isEmpty(inData) || null == outData) {
            return GvApiResult.API_PARAMS_ERROR;
        }
        int index = inData.length()/2;
        byte[] inDataTmp = GvConvertUtils.hexStringToBytes(inData);
        byte[] outDataTmp = new byte[1024];
        System.arraycopy(inDataTmp, 0, outDataTmp, 0, inData.length()/2);
        outDataTmp[index++] = (byte) 0x80;
        while(index % 8 > 0){
            outDataTmp[index++] = 0x00;
        }
        outData[0] = GvConvertUtils.bytesToHexString(outDataTmp, index);
        return GvApiResult.SUCCESS;
    }

    /**
     * 更新文件
     * @param params 更新文件参数对象
     * @return GvApiResult : 0成功，非0失败
     * @throws Exception
     */
    public GvApiResult updateFile(UpdateFileParams params) throws Exception {
        if (null == params){
            return GvApiResult.API_PARAMS_ERROR;
        }

        //预先进入PSAM卡目录
        GvApiResult result = this.enterDir(GvDevOptConstant.CHANNEL_TYPE_DSRC, params.getPsamSlot(), params.getPsamDir());
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "[EtcDevOpt.updateFile] Enter sam directory "+ params.getPsamDir() +" failed." );
            return result;
        }

        //复位卡片
        if (params.getChannelType() == GvDevOptConstant.CHANNEL_TYPE_DSRC
                && params.getChannelID() == GvDevOptConstant.DSRC_CHANNEL_ICC) {
            int ret = sdk.moduleReset((byte) GvDevOptConstant.DSRC_CHANNEL_ICC);
            if (ret != 0) {
                Log.e(LOG_FLAG, "[EtcDevOpt.updateFile] Module reset failed, error code: " + ret);
                return GvApiResult.ETC_MODULE_RESET_FAILED;
            }
            Log.i(LOG_FLAG, "Module reset success.");
        }

        //进入obu/icc目录
        result = this.enterDir(params.getChannelType(), params.getChannelID(), params.getUpdateFileDir());
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "[EtcDevOpt.updateFile] Enter directory "+ params.getUpdateFileDir() +"failed." );
            return result;
        }

        //make write file instruct: 04 D6 81 1A 05 01
        byte needMacFlag = (byte) (params.getNeedMac() ? 0x04 : 0x00);
        Log.i(LOG_FLAG, "[EtcDevOpt.updateFile] inData : "+ params.getInData());
        String instData = String.format("%02xD6%02x%02x%02x%s",
                needMacFlag,
                (byte)(0x80|params.getFid()),
                (byte) params.getStart(),
                (byte)(params.getInData().length()/2 + needMacFlag),
                params.getInData());
        Log.i(LOG_FLAG, "[EtcDevOpt.updateFile] Write file instruct : "+ instData);

        String macData = "";
        //补8的倍数
        if(params.getNeedMac()){
            //get chanllenge，8字节
            String[] randNum = new String[1];
            result = this.getChallenge(params.getChannelType(), params.getChannelID(), (byte) 4, randNum);
            if(result != GvApiResult.SUCCESS){
                Log.e(LOG_FLAG, "[EtcDevOpt.updateFile] Get challenge failed." );
                return result;
            }
            randNum[0] += "00000000";
            Log.i(LOG_FLAG, "[EtcDevOpt.updateFile] Get challenge : " + randNum[0]);

            //make mac input data
            //calMacData: 04 D6 81 1A 05 01 80 00
            String calMacData[] = new String[1];
            result = this.makeMacInput(instData, calMacData);
            if(result != GvApiResult.SUCCESS){
                return result;
            }
            Log.i(LOG_FLAG, "[EtcDevOpt.updateFile] Make mac input data : " + calMacData[0]);

            //分散密钥
            result = this.localPsamDivKey(params.getPsamSlot(), params.getKeyID(), params.getDivData());
            if(result != GvApiResult.SUCCESS){
                Log.e(LOG_FLAG, "[EtcDevOpt.updateFile] psamDivKey return failed. error code : " + result.getCode());
                return result;
            }

            //计算MAC
            String tmpMacRsp[] = new String[1];
            result = this.localPsamCalcMAC(params.getPsamSlot(), randNum[0], calMacData[0], 4, tmpMacRsp);
            if(result != GvApiResult.SUCCESS){
                Log.e(LOG_FLAG, "[EtcDevOpt.updateFile] psamCalcMAC4 return failed. error code : " + result.getCode());
                return result;
            }
            macData = tmpMacRsp[0];
            Log.i(LOG_FLAG, "[EtcDevOpt.updateFile] macData : " + macData);
        }

        //write file
        String[] cosCmds = new String[1];
        int cosNums = 0;
        cosCmds[cosNums++] = instData;
        if(params.getNeedMac()){
            cosCmds[cosNums-1] += macData;
        }
        return this.cosChannel(params.getChannelType(), params.getChannelID(), cosNums, cosCmds);
    }

    /**
     * 更新卡片0015文件
     * @param psamSlot 计算写标签信息MAC的psam卡槽号，如2
     * @param psamDir 计算写标签信息MAC的psam目录，如"DF01"
     * @param psamKeyId 计算写标签系统信息MAC的密钥索引，如"4801"
     * @param divData 分散因子，8字节卡片内部编号+4字节供应商编码+4字节供应商编码
     * @param cardInfo 卡片0015文件信息
     * @return GvApiResult : 0(成功) | 其他(失败)
     * @throws Exception
     */
    public GvApiResult updateCardFile0015 (
            int psamSlot,
            String psamDir,
            String psamKeyId,
            String divData,
            CardInformation cardInfo) throws Exception {
        if (TextUtils.isEmpty(psamDir)
                || TextUtils.isEmpty(psamKeyId)
                || null == divData
                || null == cardInfo) {
            return GvApiResult.API_PARAMS_ERROR;
        }

        //update file 0015
        byte[] vechileNumBytes = new byte[12];
        for (int i = 0; i < vechileNumBytes.length; i++) {
            vechileNumBytes[i] = 0x00;
        }

        byte[] tmpBytes;
        String vechileNumHexStr;
        try {
            tmpBytes = GvConvertUtils.stringToByte(cardInfo.getVehicleNumber());
            System.arraycopy(tmpBytes, 0, vechileNumBytes, 0, tmpBytes.length);
            vechileNumHexStr = GvConvertUtils.bytesToHexString(vechileNumBytes, vechileNumBytes.length);

        } catch (Exception e) {
            throw e;
        }
        Log.i(LOG_FLAG, "vechileNumHexStr: " + vechileNumHexStr);

        String file0015HexStr;
        if (cardInfo.getCardVersion().substring(0, 1).equals("4")) {
            Log.i(LOG_FLAG, "doCardPubTwo, the card's version is ["+ cardInfo.getCardVersion() +"], it is new GB version, so write the plate's color and model at the index of 42/43 in file of 0015.");
            file0015HexStr = cardInfo.getSignedDate()
                    + cardInfo.getExpiredDate()
                    + vechileNumHexStr
                    + cardInfo.getUserType()
                    + cardInfo.getPlateColor()
                    + cardInfo.getVehicleModel();
        } else {
            Log.i(LOG_FLAG, "doCardPubTwo, the card's version is ["+ cardInfo.getCardVersion() +"], it is old GB version, so just write the plate's color at the index of 43 in file of 0015.");
            file0015HexStr = cardInfo.getSignedDate()
                    + cardInfo.getExpiredDate()
                    + vechileNumHexStr
                    + cardInfo.getUserType()
                    + "00"
                    + cardInfo.getPlateColor();
        }
        Log.i(LOG_FLAG, "file0015HexStr: " + file0015HexStr);
        //更新0015文件
        UpdateFileParams params = new UpdateFileParams();
        params.setChannelType(GvDevOptConstant.CHANNEL_TYPE_DSRC);
        params.setChannelID( GvDevOptConstant.DSRC_CHANNEL_ICC);
        params.setPsamSlot(psamSlot);
        params.setPsamDir(psamDir);
        params.setUpdateFileDir("1001");
        params.setKeyID(psamKeyId);
        params.setFid(0x15);
        params.setDivData(divData);
        params.setStart(20);
        params.setInData(file0015HexStr);
        params.setNeedMac(true);
        GvApiResult result = updateFile(params);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "Update file '0015' failed, error code: " + result.getCode());
        } else {
            Log.i(LOG_FLAG, "Update file '0015' success. ");
        }
        return result;
    }

    /**
     * 更新卡片0016文件
     * @param psamSlot 计算写标签信息MAC的psam卡槽号，如2
     * @param psamDir 计算写标签信息MAC的psam目录，如"DF01"
     * @param psamKeyId 计算写标签系统信息MAC的密钥索引，如"4801"
     * @param divData 分散因子，8字节卡片内部编号+4字节供应商编码+4字节供应商编码
     * @param cardOwner 卡片持卡人信息
     * @return GvApiResult : 0(成功) | 其他(失败)
     * @throws Exception
     */
    public GvApiResult updateCardFile0016(
            int psamSlot,
            String psamDir,
            String psamKeyId,
            String divData,
            CardOwner cardOwner) throws Exception {
        if (TextUtils.isEmpty(psamDir)
                || TextUtils.isEmpty(psamKeyId)
                || null == divData
                || null == cardOwner) {
            return GvApiResult.API_PARAMS_ERROR;
        }

        //持卡人姓名
        byte[] cardOwnNameBytes = new byte[20];
        for (int i = 0; i < cardOwnNameBytes.length; i++) {
            cardOwnNameBytes[i] = (byte)0x00;
        }

        byte[] tmpCardOwnNameBytes = GvConvertUtils.stringToByte(cardOwner.getOwnerName());
        String cardOwnerNameHexStr = GvConvertUtils.bytesToHexString(tmpCardOwnNameBytes, tmpCardOwnNameBytes.length);
        //字符串长度不足40时，补全0
        cardOwnerNameHexStr = GvStringUtils.rightPad(cardOwnerNameHexStr, 40, "0");
        Log.i(LOG_FLAG, "cardOwnerNameHexStr: " + cardOwnerNameHexStr);

        //持卡人证件号码
        //字符串长度不足64时，补全0
        String cardOwnerLicenseNumHexStr = GvStringUtils.rightPad(GvConvertUtils.string2HexString(cardOwner.getOwnerLicenseNumber()), 64, "0");
        Log.i(LOG_FLAG, "cardOwnerLicenseNumHexStr: " + cardOwnerLicenseNumHexStr);

        StringBuffer file0016HexStr = new StringBuffer();
        file0016HexStr.append(cardOwner.getOwnerId())
                .append(cardOwner.getStaffId())
                .append(cardOwnerNameHexStr)
                .append(cardOwnerLicenseNumHexStr)
                .append(cardOwner.getOwnerLicenseType());
        Log.i(LOG_FLAG, "file0016HexStr: " + file0016HexStr);

        UpdateFileParams params = new UpdateFileParams();
        params.setChannelType(GvDevOptConstant.CHANNEL_TYPE_DSRC);
        params.setChannelID( GvDevOptConstant.DSRC_CHANNEL_ICC);
        params.setPsamSlot(psamSlot);
        params.setPsamDir(psamDir);
        params.setUpdateFileDir("3F00");
        params.setKeyID(psamKeyId);
        params.setFid(0x16);
        params.setDivData(divData);
        params.setStart(0);
        params.setInData(file0016HexStr.toString());
        params.setNeedMac(true);

        GvApiResult result = updateFile(params);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "Update file '0016' failed, error code: " + result.getCode());
        } else {
            Log.i(LOG_FLAG, "update file '0016' success");
        }
        return result;
    }

    /**
     * 根据COS指令获取车辆信息密文
     * @param random 随机数
     * @param outData 返回车辆信息密文
     * @return GvApiResult 0：成功 | 非0：失败
     */
    public GvApiResult getVehEncodeByCosCmd(
            String random,
            String[] outData) throws Exception {
        if (null == random
                || "".equals(random.trim())
                || null == outData) {
            return GvApiResult.API_PARAMS_ERROR;
        }
        //enter esam DF01
        String targetDir = "DF01";
        GvApiResult result = this.enterDir(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, targetDir);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG,"Enter esam directory DF01 failed." );
            return result;
        }
        Log.i(LOG_FLAG, "Use apdu '00B4...' reading vehicle information.");

        String[] cosCmds = new String[1];
        int cosNums = 0;

        //cos指令：读取车辆信息  00B400000A+8字节随机数+1字节期望读取的明文长度+00
        int readVehInfoLen = 79;
        String readEncVehCos = String.format("00B400000A%s%02X00", random, (byte)readVehInfoLen);
        Log.i(LOG_FLAG, "ReadEncVehCos: " + readEncVehCos);
        cosCmds[cosNums++] = readEncVehCos;

        result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, cosNums, cosCmds);
        if (result == GvApiResult.SUCCESS) {
            int rspCosIndex = cosNums-1;
            outData[0] = cosCmds[rspCosIndex].substring(0, cosCmds[rspCosIndex].length() - 4);
            Log.i(LOG_FLAG, "vehEncode: " + outData[0]);
        }
        return result;
    }

    /**
     * 使用getSecure元语获取车辆信息密文
     * @param random 8字节随机数
     * @param outData 返回的车辆信息密文
     * @return GvApiResult 0：成功 | 非0：失败
     */
    public GvApiResult getVehEncodeByGetSec(String random, String[] outData) {
        if (TextUtils.isEmpty(random) || null == outData) {
            return GvApiResult.API_PARAMS_ERROR;
        }
        Log.i(LOG_FLAG, "[EtcDevOpt.getVehEncodeByGetSec] Use obuGetSecure reading vehicle information.");

        String vehEncodeStr = sdk.obuGetSecure(random);
        if (vehEncodeStr == null) {
            return GvApiResult.GET_VEH_SECURE_FAILED;
        }

        outData[0] = vehEncodeStr;
        Log.i(LOG_FLAG, "[EtcDevOpt.getVehEncodeByGetSec] vehEncode: " + outData[0]);

        return GvApiResult.SUCCESS;
    }

    /**
     * 启动OBU保活机制
     * @param keepAlive 保活对象实例
     * @param channelId 1：ic卡，2：obu，如果正在操作obu，则保活channelId为1，反之，如果正在操作ic卡，则保活channelId为2
     */
    public void startKeepObuAlive(ObuKeepAlive keepAlive, int channelId) {
        keepAlive.start();
        keepAlive.setChannelID(channelId);
        Log.i("genvict", "Start keeping obu alive!");
    }

    /**
     * 停止保活
     * @param keepAlive 保活对象实例
     */
    public void stopKeepObuAlive(ObuKeepAlive keepAlive) {
        //取消obu保活
        keepAlive.setTaskStatus(1);
        //尝试40次，每次等待50ms，等待keepAlive中的保活任务完成后再停止计时器，防止后续执行更新文件时DSRC数据错乱或冲突
        int tryTimes = 40;
        while(true){
            tryTimes--;
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(keepAlive.getTaskStatus() == 2 || tryTimes < 1){
                break;
            }
        }
        keepAlive.stop();
    }

    /**
     * 获取车辆信息十六进制字符串
     * @param obuVehInfo 车辆信息类实例
     * @return String 车辆信息十六进制字符串
     */
    public String getVehInfoHexString(ObuVehInfo obuVehInfo) {
        if (null == obuVehInfo) {
            return null;
        }

        int vehInfoLen = 79;
        byte[] vehInfoBytes = new byte[vehInfoLen];
        int index = 0;

        byte[] bin = GvConvertUtils.stringToByte(obuVehInfo.getLicencePlateNum());
        System.arraycopy(bin, 0, vehInfoBytes, index, bin.length);
        index += 12;

        int plateColor = obuVehInfo.getLicencePlateColorAsInt();
        vehInfoBytes[index++] = (byte)((plateColor >> 8) & 0xFF);
        vehInfoBytes[index++] = (byte)(plateColor & 0xFF);

        vehInfoBytes[index++] = (byte)obuVehInfo.getVehicleTypeAsInt();
        vehInfoBytes[index++] = (byte)obuVehInfo.getVehicleUserTypeAsInt();

        if (obuVehInfo.getMyVehType() == 0) {
            int num = obuVehInfo.getVehicleLength();
            vehInfoBytes[index++] = (byte) ((num >> 8) & 0xFF);
            vehInfoBytes[index++] = (byte) (num & 0xFF);

            vehInfoBytes[index++] = (byte) obuVehInfo.getVehicleWidth();
            vehInfoBytes[index++] = (byte) obuVehInfo.getVehicleHeight();
            vehInfoBytes[index++] = (byte) obuVehInfo.getVehicleWheels();
            vehInfoBytes[index++] = (byte) obuVehInfo.getVehicleAxles();

            num = obuVehInfo.getVehicleWheelBase();
            vehInfoBytes[index++] = (byte) ((num >> 8) & 0xFF);
            vehInfoBytes[index++] = (byte) (num & 0xFF);

            num = obuVehInfo.getWeitghtOrSeatLimits();
            vehInfoBytes[index++] = (byte) ((num >> 16) & 0xFF);
            vehInfoBytes[index++] = (byte) ((num >> 8) & 0xFF);
            vehInfoBytes[index++] = (byte) (num & 0xFF);

            bin = GvConvertUtils.stringToByte(obuVehInfo.getVehicleDescription());
            System.arraycopy(bin, 0, vehInfoBytes, index, bin.length);
            index += 16;

            bin = GvConvertUtils.stringToByte(obuVehInfo.getVehicleEngineNumber());
            System.arraycopy(bin, 0, vehInfoBytes, index, bin.length);
            index += 16;

            int reserveLen = vehInfoLen - index;
            for (int i = 0; i < reserveLen; i++) {
                vehInfoBytes[index++] = (byte) 0xFF;
            }
        } else {
            int num = obuVehInfo.getVehicleLength();
            vehInfoBytes[index++] = (byte) ((num >> 8) & 0xFF);
            vehInfoBytes[index++] = (byte) (num & 0xFF);

            num = obuVehInfo.getVehicleWidth();
            vehInfoBytes[index++] = (byte) ((num >> 8) & 0xFF);
            vehInfoBytes[index++] = (byte) (num & 0xFF);

            num = obuVehInfo.getVehicleHeight();
            vehInfoBytes[index++] = (byte) ((num >> 8) & 0xFF);
            vehInfoBytes[index++] = (byte) (num & 0xFF);

            num = obuVehInfo.getVehicleWeightLimits();
            vehInfoBytes[index++] = (byte) ((num >> 16) & 0xFF);
            vehInfoBytes[index++] = (byte) ((num >> 8) & 0xFF);
            vehInfoBytes[index++] = (byte) (num & 0xFF);

            num = obuVehInfo.getCurbWeight();
            vehInfoBytes[index++] = (byte) ((num >> 16) & 0xFF);
            vehInfoBytes[index++] = (byte) ((num >> 8) & 0xFF);
            vehInfoBytes[index++] = (byte) (num & 0xFF);

            num = obuVehInfo.getTotalMass();
            if (num == 0) {
                vehInfoBytes[index++] = (byte) 0xFF;
                vehInfoBytes[index++] = (byte) 0xFF;
                vehInfoBytes[index++] = (byte) 0xFF;
            } else {
                vehInfoBytes[index++] = (byte) ((num >> 16) & 0xFF);
                vehInfoBytes[index++] = (byte) ((num >> 8) & 0xFF);
                vehInfoBytes[index++] = (byte) (num & 0xFF);
            }

            vehInfoBytes[index++] = (byte) obuVehInfo.getSeatLimits();

            bin = GvConvertUtils.stringToByte(obuVehInfo.getVin());
            System.arraycopy(bin, 0, vehInfoBytes, index, bin.length);
            index += 17;
            if (TextUtils.isEmpty(obuVehInfo.getVehicleDescription())) {
                for (int i = 0; i < 16; i++) {
                    vehInfoBytes[index++] = (byte) 0xFF;
                }
            } else {
                bin = GvConvertUtils.stringToByte(obuVehInfo.getVehicleDescription());
                System.arraycopy(bin, 0, vehInfoBytes, index, bin.length);
                index += 16;
            }

            int reserveLen = vehInfoLen - index;
            for (int i = 0; i < reserveLen; i++) {
                vehInfoBytes[index++] = (byte) 0xFF;
            }
        }

        return GvConvertUtils.bytesToHexString(vehInfoBytes, vehInfoLen);
    }

    /**
     *
     * @Title parseVehicleInfo
     * @Description 解析车辆信息字节数组
     * @author ZengXiaoJiang
     * @param srcData 车辆信息字节数组
     * @param obuVehInfo 输出车辆信息实例对象
     * @return GvApiResult 0：成功 | 非0：失败
     */
    public GvApiResult parseVehicleInfo(byte[] srcData, ObuVehInfo obuVehInfo) {
        if (null == srcData || null == obuVehInfo) {
            return GvApiResult.API_PARAMS_ERROR;
        }

        int index = 0;
        int readVehInfoLen = 79;
        byte[] tmp = new byte[readVehInfoLen];

        //车牌号码，12字节
        obuVehInfo.setLicencePlateNum(GvConvertUtils.byteToString(srcData, 0, 12));
        obuVehInfo.setLicencePlateNumAsHex(GvConvertUtils.bytesToHexString(srcData, 12));
        index += 12;

        //第13个字节
        int tmpIntValue = (int)srcData[++index];
        HashMap<Integer, String> tmpMap = new HashMap<>(16);
        //licence plate color, 2 bytes, high bytes : 0x00, low bytes(plate color) : 0x00~0x03
        obuVehInfo.setLicencePlateColorAsInt(tmpIntValue);
        //车牌颜色从0开始定义，目前国标有效的定义到6，其他的保留
        int plateColorMapIndex = 0;
        tmpMap.put(plateColorMapIndex++, "蓝色");
        tmpMap.put(plateColorMapIndex++, "黄色");
        tmpMap.put(plateColorMapIndex++, "黑色");
        tmpMap.put(plateColorMapIndex++, "白色");
        tmpMap.put(plateColorMapIndex++, "渐变绿色");
        tmpMap.put(plateColorMapIndex++, "黄绿双拼色");
        tmpMap.put(plateColorMapIndex++, "蓝白渐变色");
        if (tmpMap.containsKey(tmpIntValue)) {
            obuVehInfo.setLicencePlateColor(tmpMap.get(tmpIntValue));
        } else {
            obuVehInfo.setLicencePlateColor("未定义");
        }
        index++; //定位到下一个字节

        //vehicle type
        tmpIntValue = (int)srcData[index++];
        tmpMap.clear();
        obuVehInfo.setVehicleTypeAsInt(tmpIntValue);
        tmpMap.put(1, "一类客车");
        tmpMap.put(2, "二类客车");
        tmpMap.put(3, "三类客车");
        tmpMap.put(4, "四类客车");
        tmpMap.put(5, "五类客车");
        tmpMap.put(6, "六类客车");
        //新增货车的车型
        tmpMap.put(11, "一类货车");
        tmpMap.put(12, "二类货车");
        tmpMap.put(13, "三类货车");
        tmpMap.put(14, "四类货车");
        tmpMap.put(15, "五类货车");
        tmpMap.put(16, "六类货车");
        tmpMap.put(21, "一类专项作业车/牵引车");
        tmpMap.put(22, "二类专项作业车/牵引车");
        tmpMap.put(23, "三类专项作业车/牵引车");
        tmpMap.put(24, "四类专项作业车/牵引车");
        tmpMap.put(25, "五类专项作业车/牵引车");
        tmpMap.put(26, "六类专项作业车/牵引车");
        if (tmpMap.containsKey(tmpIntValue)) {
            obuVehInfo.setVehicleType(tmpMap.get(tmpIntValue));
        } else {
            obuVehInfo.setVehicleType("未定义");
        }
        int vehType = tmpIntValue;

        //user type
        tmpIntValue = (int)srcData[index++];
        tmpMap.clear();
        obuVehInfo.setVehicleUserTypeAsInt(tmpIntValue);
        tmpMap.put(0, "普通用户");
        tmpMap.put(6, "公务车用户");
        tmpMap.put(8, "军警车用户");
        tmpMap.put(10, "紧急车用户");
        tmpMap.put(12, "免费用户");
        tmpMap.put(14, "车队用户");
        //新增的货车车辆用户类型
        tmpMap.put(24, "集装箱用户");
        tmpMap.put(26, "应急救援车");
        tmpMap.put(27, "牵引车用户");

        if (tmpMap.containsKey(tmpIntValue)) {
            obuVehInfo.setVehicleUserType(tmpMap.get(tmpIntValue));
        } else {
            obuVehInfo.setVehicleUserType("未定义");
        }

        if (vehType < 11) {
            Log.i(LOG_FLAG, "解析客车信息");
            //客车，按照旧国标解析
            obuVehInfo.setMyVehType(0);
            byte[] vehicleParam = new byte[4];
            //vehicle length
            vehicleParam[0] = 0x00;
            vehicleParam[1] = 0x00;
            vehicleParam[2] = srcData[index++];
            vehicleParam[3] = srcData[index++];

            obuVehInfo.setVehicleLength(GvConvertUtils.bytesToBigInt(vehicleParam));

            //vehicle width
            vehicleParam[0] = 0x00;
            vehicleParam[1] = 0x00;
            vehicleParam[2] = 0x00;
            vehicleParam[3] = srcData[index++];
            obuVehInfo.setVehicleWidth(GvConvertUtils.bytesToBigInt(vehicleParam));

            //vehicle height
            vehicleParam[0] = 0x00;
            vehicleParam[1] = 0x00;
            vehicleParam[2] = 0x00;
            vehicleParam[3] = srcData[index++];
            obuVehInfo.setVehicleHeight(GvConvertUtils.bytesToBigInt(vehicleParam));

            //vehicle wheels
            vehicleParam[0] = 0x00;
            vehicleParam[1] = 0x00;
            vehicleParam[2] = 0x00;
            vehicleParam[3] = srcData[index++];
            obuVehInfo.setVehicleWheels(GvConvertUtils.bytesToBigInt(vehicleParam));

            //vehicle axles
            vehicleParam[0] = 0x00;
            vehicleParam[1] = 0x00;
            vehicleParam[2] = 0x00;
            vehicleParam[3] = srcData[index++];
            obuVehInfo.setVehicleAxles(GvConvertUtils.bytesToBigInt(vehicleParam));

            //vehicle axles
            vehicleParam[0] = 0x00;
            vehicleParam[1] = 0x00;
            vehicleParam[2] = srcData[index++];
            vehicleParam[3] = srcData[index++];
            obuVehInfo.setVehicleWheelBase(GvConvertUtils.bytesToBigInt(vehicleParam));

            //vehicle weitght or seat limits
            vehicleParam[0] = 0x00;
            vehicleParam[1] = srcData[index++];
            vehicleParam[2] = srcData[index++];
            vehicleParam[3] = srcData[index++];
            obuVehInfo.setWeitghtOrSeatLimits(GvConvertUtils.bytesToBigInt(vehicleParam));

            //vehicle description
            System.arraycopy(srcData, index, tmp, 0, 16);
            obuVehInfo.setVehicleDescription(GvConvertUtils.byteToString(tmp, 0, 16));
            index += 16;

            //vehicle engine number
            System.arraycopy(srcData, index, tmp, 0, 16);
            obuVehInfo.setVehicleEngineNumber(GvConvertUtils.byteToString(tmp, 0, 16));
            index += 16;
        } else {
            Log.i(LOG_FLAG, "解析货车信息");
            //货车，按照货车新规定义解析
            obuVehInfo.setMyVehType(1);
            byte[] vehicleParam = new byte[4];
            //vehicle length
            vehicleParam[0] = 0x00;
            vehicleParam[1] = 0x00;
            vehicleParam[2] = srcData[index++];
            vehicleParam[3] = srcData[index++];

            obuVehInfo.setVehicleLength(GvConvertUtils.bytesToBigInt(vehicleParam));

            //vehicle width
            vehicleParam[0] = 0x00;
            vehicleParam[1] = 0x00;
            vehicleParam[2] = srcData[index++];
            vehicleParam[3] = srcData[index++];
            obuVehInfo.setVehicleWidth(GvConvertUtils.bytesToBigInt(vehicleParam));

            //vehicle height
            vehicleParam[0] = 0x00;
            vehicleParam[1] = 0x00;
            vehicleParam[2] = srcData[index++];
            vehicleParam[3] = srcData[index++];
            obuVehInfo.setVehicleHeight(GvConvertUtils.bytesToBigInt(vehicleParam));

            //车辆核定载质量/准牵引总质量，3字节，千克
            vehicleParam[0] = 0x00;
            vehicleParam[1] = srcData[index++];
            vehicleParam[2] = srcData[index++];
            vehicleParam[3] = srcData[index++];
            obuVehInfo.setVehicleWeightLimits(GvConvertUtils.bytesToBigInt(vehicleParam));

            //整备质量，3字节，千克
            vehicleParam[0] = 0x00;
            vehicleParam[1] = srcData[index++];
            vehicleParam[2] = srcData[index++];
            vehicleParam[3] = srcData[index++];
            obuVehInfo.setCurbWeight(GvConvertUtils.bytesToBigInt(vehicleParam));

            //车辆总质量，3字节，千克
            vehicleParam[0] = 0x00;
            vehicleParam[1] = srcData[index++];
            vehicleParam[2] = srcData[index++];
            vehicleParam[3] = srcData[index++];
            obuVehInfo.setTotalMass(GvConvertUtils.bytesToBigInt(vehicleParam));

            //核定载人数，1字节
            obuVehInfo.setSeatLimits(srcData[index++]);
            //车辆识别代号
            obuVehInfo.setVin(GvConvertUtils.byteToString(srcData, index, 17));
            index += 17;
            //车辆特征描述
            obuVehInfo.setVehicleDescription(GvConvertUtils.byteToString(srcData, index, 16));
            index += 16;
        }

        //保留
        int reserveLen = readVehInfoLen - index;
        if (reserveLen > 0) {
            System.arraycopy(srcData, index, tmp, 0, reserveLen);
            obuVehInfo.setReserve(GvConvertUtils.bytesToHexString(tmp, reserveLen));
        }

        return GvApiResult.SUCCESS;
    }

    /**
     * 获取本地PSAM终端机编号
     * @author ZengXiaoJiang
     * @param slot psam卡槽号，对应dsrc模块psam通道号
     * @param outData 输出psam返回的终端机编号
     * @return GvApiResult : 0成功，非0失败
     */
    public GvApiResult localPsamGetTerminateID(int slot, String[] outData) throws Exception {
        if (null == outData || slot<1 || slot > 4) {
            return GvApiResult.API_PARAMS_ERROR;
        }
        String[] cosCmds = new String[2];
        int cosNums = 0;

        //指令1：进入3F00 目录 00A40000023F00
        cosCmds[cosNums++] = "00A40000023F00";
        //指令2，读取PSAM终端机编号  00B0960006
        cosCmds[cosNums++] = "00B0960006";
        //执行指令
        GvApiResult result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, slot , cosNums, cosCmds);
        if (result == GvApiResult.SUCCESS) {
            int rspCosIndex = cosNums-1;
            outData[0] = cosCmds[rspCosIndex].substring(0, cosCmds[rspCosIndex].length()-4);
        }
        return result;
    }

    /**
     * 本地psam分散密钥
     * @param slot psam卡槽位置
     * @param keyID 密钥索引，例如5903 、3903等
     * @param divData 分散因子，一级分散长度8个字节，二级分散长度16字节
     * @return GvApiResult : 0成功，非0失败
     * @throws Exception
     */
    public GvApiResult localPsamDivKey(int slot, String keyID, String divData) throws Exception{
        if((keyID.length() != 4)
                || (slot < 1)
                || (divData == null)
                || (divData.length() != 16 && divData.length() != 32)){
            return GvApiResult.API_PARAMS_ERROR;
        }

        byte[] keyIDArray = GvConvertUtils.hexStringToBytes(keyID);
        int divDataLen = keyIDArray[0] > (byte)0x39 ? 16 : 8;
        String[] cosCmds = new String[1];
        int cosNums = 0;
        //指令1, PSAM分散密钥 801A590310xxxx
        cosCmds[cosNums++] = String.format("801A%s%02X%s", keyID, divDataLen, divData.substring(0, 2*divDataLen));
        return this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, slot, cosNums, cosCmds);
    }

    /**
     * 本地psam计算MAC(4字节/8字节)
     * @param slot psam卡槽位置
     * @param initData 初始化数据（随机数）
     * @param inData 待计算mac的数据
     * @param macLen MAC的长度
     * @param outData 输出MAC数据
     * @return GvApiResult : 0成功，非0失败
     * @throws Exception
     */
    public GvApiResult localPsamCalcMAC(int slot, String initData, String inData, int macLen, String[] outData) throws Exception{
        if(slot<1 || slot > 4
                || TextUtils.isEmpty(initData)
                || TextUtils.isEmpty(inData)
                || (macLen != 4 && macLen != 8)){
            return GvApiResult.API_PARAMS_ERROR;
        }
        String[] cosCmds = new String[1];
        int cosNums = 0;
        if (macLen == 4) {
            //PSAM计算MAC：80FA0500 +len + initData + inData
            cosCmds[cosNums++] = String.format(Locale.CHINESE, "80FA0500%02X%s%s", (byte) (inData.length() / 2 + 8), initData, inData);
        } else if (macLen == 8) {
            //PSAM计算MAC：80FA0800 +len + initData + inData
            cosCmds[cosNums++] = String.format(Locale.CHINESE, "80FA0800%02X%s%s", (byte) (inData.length() / 2 + 8), initData, inData);
        }
        GvApiResult result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, slot, cosNums, cosCmds);
        if (result == GvApiResult.SUCCESS) {
            int rspCosIndex = cosNums-1;
            outData[0] = cosCmds[rspCosIndex].substring(0, cosCmds[rspCosIndex].length()-4);
            Log.i(LOG_FLAG,  "MAC, length: " + (outData[0].length() >> 1) + "data:"+ outData[0]);
        }
        return result;
    }

    /**
     * 本地psam解密 80FA8000 +len +data
     * @param slot psam卡槽位置
     * @param inData 密文
     * @param outData 输出明文
     * @return GvApiResult : 0成功，非0失败
     * @throws Exception
     */
    public GvApiResult localPsamDecode(int slot, String inData, String[] outData) throws Exception {
        if(slot<1 || slot > 4 || inData == null || outData == null){
            return GvApiResult.API_PARAMS_ERROR;
        }

        String[] cosCmds = new String[1];
        int cosNums = 0;

        //指令1, 解密 80FA8000 +len +data
        cosCmds[cosNums++] = String.format("80FA8000%02X%s", (byte)(inData.length() >> 1), inData);
        GvApiResult result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, slot, cosNums, cosCmds);
        if (result == GvApiResult.SUCCESS) {
            int rspCosIndex = cosNums-1;
            outData[0] = cosCmds[rspCosIndex].substring(0, cosCmds[rspCosIndex].length()-4);
            Log.i(LOG_FLAG,  "DecodeVeh, length: " + (outData[0].length() >> 1) + "data: "+ outData[0]);
        }
        return result;
    }

    /**
     * 本地检测标签
     * @param psamSlot 解密车辆信息的PSAM卡槽
     * @param psamDir 解密车辆信息的PSAM目录，例如DF01
     * @param psamKeyId 解密车辆信息的密钥索引，例如5903
     * @param vehInfoLen 读取的车辆信息明文长度，最长79字节
     * @param obuSysInfo 标签系统信息
     * @param obuVehInfo 车辆信息
     * @param cardInfo 卡片0015文件信息
     * @return GvApiResult : 0成功，非0失败
     * @throws Exception
     */
    public GvApiResult localCheckObu(
            int psamSlot,
            String psamDir,
            String psamKeyId,
            int vehInfoLen,
            ObuSystemInformation obuSysInfo,
            ObuVehInfo obuVehInfo,
            CardInformation cardInfo) throws Exception {
        if(TextUtils.isEmpty(psamDir)
                || TextUtils.isEmpty(psamKeyId)
                || vehInfoLen <= 0
                || vehInfoLen > 79
                || obuVehInfo == null){
            return GvApiResult.API_PARAMS_ERROR;
        }
        //打开设备DSRC
        GvApiResult result = GvApiResult.SUCCESS;
//        if (!isOpenDsrc) {
//            result = this.openDsrc();//开天线
//        }
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }
        //搜索OBU
        result = this.findObu(obuSysInfo, cardInfo);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        //分散因子
        String divData = obuSysInfo.getSerialNumber()
                + obuSysInfo.getxsSysInfo().substring(0, 8)
                + obuSysInfo.getxsSysInfo().substring(0, 8);
        //随机数
        String random = "0000000000000000";
        //执行本地解密车辆信息
        result = doLocalDecodeVehInfo(
                psamSlot,
                psamDir,
                psamKeyId,
                vehInfoLen,
                random,
                divData,
                obuVehInfo);
        //释放OBU
        sdk.obuRelease(result == GvApiResult.SUCCESS, true);
        //关闭DSRC
//        this.closeDsrc();

        return result;
    }

    /**
     * 本地激活标签
     * @param psamSlot 计算写标签信息MAC的psam卡槽号，如2
     * @param psamDir 计算写标签信息MAC的psam目录，如"DF01"
     * @param psamKeyIdForSysMac 计算写标签系统信息MAC的密钥索引，如"2601"
     * @param obuSysInfo 搜索到的标签的系统信息
     * @param cardInfo 搜索到的卡片的0015文件信息
     * @return GvApiResult : 0成功，非0失败
     * @throws Exception
     */
    public GvApiResult localActivateObu(
            int psamSlot,
            String psamDir,
            String psamKeyIdForSysMac,
            ObuSystemInformation obuSysInfo,
            CardInformation cardInfo) throws Exception {
        if (obuSysInfo == null || cardInfo == null) {
            return GvApiResult.API_PARAMS_ERROR;
        }
        //打开设备DSRC
        GvApiResult result = GvApiResult.SUCCESS;
//        if (!isOpenDsrc) {
//            result = this.openDsrc();//开天线
//        }
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }
        //搜索OBU
        result = this.findObu(obuSysInfo, cardInfo);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        Log.i(LOG_FLAG, "Find obu: " + obuSysInfo.getSerialNumber());
        if (cardInfo.getStatus()) {
            //检测到卡片
            Log.i(LOG_FLAG, "Find card, card id: " + cardInfo.getCardId() + "\r\n"
                    + "card version: " + cardInfo.getCardVersion());
        } else {
            //未检测到卡片
            return GvApiResult.CARD_NO_FIND;
        }
        //分散因子
        String divData = obuSysInfo.getSerialNumber()
                + obuSysInfo.getxsSysInfo().substring(0, 8)
                + obuSysInfo.getxsSysInfo().substring(0, 8);
        //系统信息：写拆卸位(26)为1即可。
        int start = 26;
        String writeFileData = "01";
        int fid = 1;
        String updateFileDir = "3F00";
        UpdateFileParams params = new UpdateFileParams();
        params.setChannelType(GvDevOptConstant.CHANNEL_TYPE_OBU);
        params.setChannelID(GvDevOptConstant.OBU_CHANNEL_ESAM);
        params.setPsamSlot(psamSlot);
        params.setPsamDir(psamDir);
        params.setUpdateFileDir(updateFileDir);
        params.setKeyID(psamKeyIdForSysMac);
        params.setFid(fid);
        params.setDivData(divData);
        params.setStart(start);
        params.setInData(writeFileData);
        params.setNeedMac(true);
        //更新文件
        result = this.updateFile(params);
        if (result == GvApiResult.SUCCESS) {
            //对应vst中的obu status的第二个byte：0未拆卸，1已拆卸，故已拆卸对应一个字节：0000 0010，为2
            obuSysInfo.setTearFlag((byte) 0);
            Log.i(LOG_FLAG, "localActivateObu: 激活成功！");
        }
        //释放OBU
        sdk.obuRelease(result == GvApiResult.SUCCESS, true);
        //关闭DSRC
//        this.closeDsrc();

        return result;
    }

    /**
     * 本地二次发行标签
     * @param psamSlot 计算写标签信息MAC的psam卡槽号，如2
     * @param psamDir 计算写标签信息MAC的psam目录，如"DF01"
     * @param psamKeyIdForSysMac 计算写标签系统信息MAC的密钥索引，如"2601"
     * @param psamKeyIdForVehMac 计算写标签车辆信息MAC的密钥索引，如"2602"
     * @param signedDate 待更新的标签系统信息中的合同签署日期
     * @param expiredDate 待更新的标签系统信息中的合同失效日期
     * @param obuVehInfo 待更新的标签车辆信息
     * @return GvApiResult : 0成功，非0失败
     * @throws Exception
     */
    public GvApiResult localSecIssueObu(
            int psamSlot,
            String psamDir,
            String psamKeyIdForSysMac,
            String psamKeyIdForVehMac,
            String signedDate,
            String expiredDate,
            ObuVehInfo obuVehInfo) throws Exception {
        if (TextUtils.isEmpty(signedDate)
                || TextUtils.isEmpty(expiredDate)
                || obuVehInfo == null) {
            return GvApiResult.API_PARAMS_ERROR;
        }
        //打开设备DSRC
        GvApiResult result = GvApiResult.SUCCESS;
//        if (!isOpenDsrc) {
//            result = this.openDsrc();//开天线
//        }
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }
        //搜索OBU
        ObuSystemInformation obuSysInfo = new ObuSystemInformation();
        CardInformation cardInfo = new CardInformation();
        result = this.findObu(obuSysInfo, cardInfo);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        Log.i(LOG_FLAG, "Find obu: " + obuSysInfo.getSerialNumber());
        if (cardInfo.getStatus()) {
            //检测到卡片
            Log.i(LOG_FLAG, "Find card, card id: " + cardInfo.getCardId() + "\r\n"
                    + "card version: " + cardInfo.getCardVersion());
        } else {
            //未检测到卡片
            return GvApiResult.CARD_NO_FIND;
        }
        //分散因子
        String divData = obuSysInfo.getSerialNumber()
                + obuSysInfo.getxsSysInfo().substring(0, 8)
                + obuSysInfo.getxsSysInfo().substring(0, 8);

        //更新车辆信息
        int start = 0;
        int fid = 1;
        String updateFileDir = "DF01";
        String writeFileData = this.getVehInfoHexString(obuVehInfo);
        Log.i(LOG_FLAG, "update vehicle info, writeFileData: " + writeFileData);

        UpdateFileParams params = new UpdateFileParams();
        params.setChannelType(GvDevOptConstant.CHANNEL_TYPE_OBU);
        params.setChannelID( GvDevOptConstant.OBU_CHANNEL_ESAM);
        params.setPsamSlot(psamSlot);
        params.setPsamDir(psamDir);
        params.setUpdateFileDir(updateFileDir);
        params.setKeyID(psamKeyIdForVehMac);
        params.setFid(fid);
        params.setDivData(divData);
        params.setStart(start);
        params.setInData(writeFileData);
        params.setNeedMac(true);

        result = this.updateFile(params);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "更新标签车辆信息失败, error code: " + result.getCode());
        }
        Log.i(LOG_FLAG, "更新标签车辆信息成功！");

        //更新标签系统信息：签署日期+过期日期+防拆信息（00：已拆卸，01：未拆卸）
        start = 18;
        fid = 1;
        updateFileDir = "3F00";
        writeFileData = signedDate + expiredDate + "00";
        Log.i(LOG_FLAG, "update system info, writeFileData: " + writeFileData);

        params.setChannelType(GvDevOptConstant.CHANNEL_TYPE_OBU);
        params.setChannelID(GvDevOptConstant.OBU_CHANNEL_ESAM);
        params.setPsamSlot(psamSlot);
        params.setPsamDir(psamDir);
        params.setUpdateFileDir(updateFileDir);
        params.setKeyID(psamKeyIdForSysMac);
        params.setFid(fid);
        params.setDivData(divData);
        params.setStart(start);
        params.setInData(writeFileData);
        params.setNeedMac(true);

        result = this.updateFile(params);
        if (result == GvApiResult.SUCCESS) {
            Log.i(LOG_FLAG, "更新标签系统信息成功！");
        }

        //释放OBU
        sdk.obuRelease(result == GvApiResult.SUCCESS, true);
        //关闭DSRC
//        this.closeDsrc();

        return result;
    }

    /**
     * 本地卡片二次发行
     * @param psamSlot 计算写标签信息MAC的psam卡槽号，如2
     * @param psamDir 计算写标签信息MAC的psam目录，如"DF01"
     * @param psamKeyId 计算写标签系统信息MAC的密钥索引，如"4801"
     * @param cardInfo 卡片0015文件信息
     * @param cardOwner 卡片0016文件信息
     * @return GvApiResult : 0(成功) | 其他(失败)
     * @throws Exception
     */
    public GvApiResult localSecIssueCard(
            int psamSlot,
            String psamDir,
            String psamKeyId,
            CardInformation cardInfo,
            CardOwner cardOwner) throws Exception {
        if (TextUtils.isEmpty(psamDir)
                || TextUtils.isEmpty(psamKeyId)
                || cardInfo == null
                || cardOwner == null) {
            return GvApiResult.API_PARAMS_ERROR;
        }
        //打开设备DSRC
        GvApiResult result = GvApiResult.SUCCESS;
//        if (!isOpenDsrc) {
//            result = this.openDsrc();//开天线
//        }
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }
        //执行本地卡片二次发行
        result = doLocalSecIssueCard(psamSlot, psamDir, psamKeyId, cardInfo, cardOwner);
        //关闭DSRC
//        this.closeDsrc();
        return result;
    }

    /**
     * 卡片消费
     * @param consumeMethod 消费模式，复合消费：GvDevOptConstant.CONSUME_METHOD_COMPLEX，简单消费：GvDevOptConstant.CONSUME_METHOD_SIMPLE
     * @param consumeChannel 消费通道，DSRC 13.56M通道：GvDevOptConstant.CHANNEL_TYPE_DSRC， OBU 5.8G通道：GvDevOptConstant.CHANNEL_TYPE_OBU
     * @param psamSlot 消费PSAM的卡槽，对应设备的卡槽号：1~4号
     * @param psamDir 消费PSAM目录，如"DF01"
     * @param payMoney 消费金额
     * @param record0019 0019文件记录信息
     * @param rspTransInfo 响应的交易信息对象实例
     * @return GvApiResult: 0成功，非0失败
     * @throws Exception
     */
    public GvApiResult localCardConsume(
            final int consumeMethod,
            final int consumeChannel,
            final int psamSlot,
            final String psamDir,
            final int payMoney,
            final String record0019,
            TransInfo rspTransInfo) throws Exception{
        //打开天线模块
        GvApiResult result = GvApiResult.SUCCESS;
//        if (!isOpenDsrc) {
//            result = this.openDsrc();//开天线
//        }
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }

        //根据消费策略执行卡片消费
        result = cardConsumeStrategy(
                consumeMethod,
                consumeChannel,
                psamSlot,
                psamDir,
                payMoney,
                record0019,
                rspTransInfo);
        //如果是走5.8G通道消费，最后需要释放OBU
        if (consumeChannel == GvDevOptConstant.CHANNEL_TYPE_OBU) {
            //only obu channel need to release obu
            sdk.obuRelease(result == GvApiResult.SUCCESS, true);
        }
        //关闭天线模块
//        this.closeDsrc();
        return result;
    }

    /**
     * 写CPC卡出入口信息
     * @param area 区域
     * @param cpcEntryExitInfo cpc出入口信息
     * @return GvApiResult: 0成功，非0失败
     * @throws Exception
     */
    public GvApiResult writeCpcEntryExitInfo(int area, CpcEntryExitInfo cpcEntryExitInfo) throws Exception {
        if (cpcEntryExitInfo == null) {
            return GvApiResult.API_PARAMS_ERROR;
        }
        //打开dsrc
        GvApiResult result = GvApiResult.SUCCESS;
//        if (!isOpenDsrc) {
//            result = this.openDsrc();//开天线
//        }
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }
        //执行业务
        result = this.doWriteCpcEntryExitInfo(area, cpcEntryExitInfo);
        //关闭dsrc
//        this.closeDsrc();
        return result;
    }

    /**
     * 读CPC卡出入口信息
     * @param cpcEntryExitInfo CPC卡出入口信息
     * @return GvApiResult: 0成功，非0失败
     * @throws Exception
     */
    public GvApiResult readCpcEntryExitInfo(CpcEntryExitInfoMDL cpcEntryExitInfo, CPCEF02EF04InfoMDL cpcef02EF04InfoMDL) throws Exception {
        if (cpcEntryExitInfo == null) {
            return GvApiResult.API_PARAMS_ERROR;
        }
        //打开dsrc
        GvApiResult result = GvApiResult.SUCCESS;
//        if (!isOpenDsrc) {
//            result = this.openDsrc();//开天线
//        }
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }
        //执行业务
        result = this.doReadCpcEntryExitInfo(cpcEntryExitInfo, cpcef02EF04InfoMDL);
        //关闭dsrc
//        this.closeDsrc();
        return result;
    }

    /**
     * 卡片消费策略
     * @param consumeMethod 消费模式，复合消费：GvDevOptConstant.CONSUME_METHOD_COMPLEX，简单消费：GvDevOptConstant.CONSUME_METHOD_SIMPLE
     * @param consumeChannel 消费通道，DSRC 13.56M通道：GvDevOptConstant.CHANNEL_TYPE_DSRC， OBU 5.8G通道：GvDevOptConstant.CHANNEL_TYPE_OBU
     * @param psamSlot 消费PSAM的卡槽，useCloudPsam为true时，该参数无效，云盒不需要指定psam卡槽
     * @param psamDir 消费PSAM目录，如"DF01"
     * @param payMoney 消费金额
     * @param record0019 0019文件记录信息
     * @param rspTransInfo 响应的交易信息对象实例
     * @return GvApiResult: 0成功，非0失败
     * @throws Exception
     */
    private GvApiResult cardConsumeStrategy(
            final int consumeMethod,
            final int consumeChannel,
            final int psamSlot,
            final String psamDir,
            final int payMoney,
            final String record0019,
            TransInfo rspTransInfo) throws Exception {
        //当消费模式为复合消费的时候，检查0019文件记录参数
        if (consumeMethod == GvDevOptConstant.CONSUME_METHOD_COMPLEX && TextUtils.isEmpty(record0019)) {
            return GvApiResult.API_PARAMS_ERROR;
        }

        if (consumeChannel == GvDevOptConstant.CHANNEL_TYPE_OBU) {
            //OBU 5.8G通道
            //搜索OBU
            ObuSystemInformation obuSysInfo = new ObuSystemInformation();
            CardInformation cardInfo = new CardInformation();
            GvApiResult result = this.findObu(obuSysInfo, cardInfo);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //保存OBU MAC地址
            rspTransInfo.setObuId(obuSysInfo.getObuId());
            //判断OBU是否插卡
            if (!cardInfo.getStatus()) {
                return GvApiResult.CARD_NO_FIND;
            }
            //执行消费
            return this.doCardConsume(
                    consumeMethod,
                    consumeChannel,
                    GvDevOptConstant.OBU_CHANNEL_ICC,
                    psamSlot,
                    psamDir,
                    cardInfo,
                    payMoney,
                    record0019,
                    rspTransInfo);
        } else if (consumeChannel == GvDevOptConstant.CHANNEL_TYPE_DSRC) {
            //DSRC 13.56M通道
            int ret = sdk.moduleReset((byte) 0x0);
            if (ret != 0) {
                return GvApiResult.ETC_MODULE_RESET_FAILED;
            }
            //读取卡片0015文件信息
            CardInformation cardInfo = new CardInformation();
            ret = sdk.getCardInformation(cardInfo);
            if (ret != 0) {
                return GvApiResult.CARD_NO_FIND;
            }
            //执行消费
            return this.doCardConsume(
                    consumeMethod,
                    consumeChannel,
                    GvDevOptConstant.DSRC_CHANNEL_ICC,
                    psamSlot,
                    psamDir,
                    cardInfo,
                    payMoney,
                    record0019,
                    rspTransInfo);
        } else {
            return GvApiResult.UNKNOWN_ERROR;
        }
    }

    /**
     * 执行卡片消费
     * @param consumeMethod 消费模式，复合消费：GvDevOptConstant.CONSUME_METHOD_COMPLEX，简单消费：GvDevOptConstant.CONSUME_METHOD_SIMPLE
     * @param consumeChannel 消费通道，DSRC 13.56M通道：GvDevOptConstant.CHANNEL_TYPE_DSRC， OBU 5.8G通道：GvDevOptConstant.CHANNEL_TYPE_OBU
     * @param channelID DSRC通道：0-ICC通道，1-SAM1，2-SAM2, 3-SAM3，4-SAM4；OBU通道：1-ICC，2-ESAM
     * @param psamSlot 消费psam卡槽号，useCloudPsam为true时，该参数无效，云盒不需要指定psam卡槽
     * @param psamDir 消费psam目录
     * @param cardInfo 卡片0015文件信息对象实例
     * @param money 消费金额
     * @param record0019 0019消费记录
     * @param rspTransInfo 返回的交易信息对象实例
     * @return GvApiResult : 0(成功) | 其他(失败)
     * @throws Exception
     */
    private GvApiResult doCardConsume(
            final int consumeMethod,
            final int consumeChannel,
            final int channelID,
            final int psamSlot,
            final String psamDir,
            CardInformation cardInfo,
            final int money,
            final String record0019,
            TransInfo rspTransInfo) throws Exception {
        //读取卡片余额
        int[] balance = new int[1];
        GvApiResult result = this.readICCBalance(consumeChannel, channelID, balance);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        cardInfo.setBalance(balance[0]);
        //检查消费金额
        Log.i(LOG_FLAG, "卡片余额：" + GvConvertUtils.getUnsignedInt(cardInfo.getBalance()) + "");
        if (GvConvertUtils.getUnsignedInt(cardInfo.getBalance()) < money) {
            return GvApiResult.CARD_INSUFFICIENT_BALANCE;
        }
        //获取psam终端机编号
        String[] psamTerminateID = new String[1];

        //使用本地消费PSAM
        //读取0016文件6字节终端机编号
        result = this.localPsamGetTerminateID(psamSlot, psamTerminateID);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "获取本地PSAM终端机编号失败，error code : " + result.getCode() + ", msg: " + result.getMsg());
            return result;
        }
        //进入PSAM消费目录
        result = this.enterDir(GvDevOptConstant.CHANNEL_TYPE_DSRC, psamSlot, psamDir);
        if (result != GvApiResult.SUCCESS) {
            Log.i(LOG_FLAG, "Enter psam dir " + psamDir + " return failed. error code : " + result.getCode());
            return result;
        }

        //保存PSAM终端机编号
        Log.i(LOG_FLAG, "PSAM终端机编号: " + psamTerminateID[0]);
        rspTransInfo.setTerminalNo(psamTerminateID[0]);

        //消费初始化
        String appType = "02";
        String keyIndex = "01";
        DebitForPurchaseInitParams debitForPurchaseInitParams = new DebitForPurchaseInitParams();
        debitForPurchaseInitParams.setConsumeMethod(consumeMethod);
        debitForPurchaseInitParams.setChannelType(consumeChannel);
        debitForPurchaseInitParams.setChannelID(channelID);
        debitForPurchaseInitParams.setCardBalance(cardInfo.getBalance());
        debitForPurchaseInitParams.setMoney(money);
        debitForPurchaseInitParams.setTerminalNo(psamTerminateID[0]);
        debitForPurchaseInitParams.setAppType(appType);
        debitForPurchaseInitParams.setKeyIndex(keyIndex);
        //执行消费初始化
        result = debitForPurchaseInit(debitForPurchaseInitParams);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "消费初始化失败, error code: " + result.getCode() + ", msg: " + result.getMsg());
            return result;
        }
        //2字节ICC交易序号
        String iccPaySerial = debitForPurchaseInitParams.getOutData().substring(8, 12);
        rspTransInfo.setIccPaySerial(iccPaySerial);
        //4字节伪随机数
        String iccRand =  debitForPurchaseInitParams.getOutData().substring(22, 30);
        Log.i(LOG_FLAG, "iccPaySerial : " + iccPaySerial + "\n" + "iccRand : " + iccRand);

        //计算消费MAC1
        String payMoney = GvConvertUtils.bytesToHexString(GvConvertUtils.bigIntToBytes(money), 4);
        String timeStampNow = GvTimeUtils.timeStampToDateString(GvTimeUtils.getUnixTimestamp(), "yyyyMMddHHmmss");
        //保存交易时间
        rspTransInfo.setTransTime(timeStampNow);
        Log.i(LOG_FLAG, "payMoney: " + payMoney);
        Log.i(LOG_FLAG, "Calc mac1, timeStampNow : " + timeStampNow);
        //构造cardConsumeCalcMac1参数
        CardConsumeCalcMac1Params cardConsumeCalcMac1Params = new CardConsumeCalcMac1Params();
        cardConsumeCalcMac1Params.setConsumeMethod(consumeMethod);
        cardConsumeCalcMac1Params.setPsamSlotForPurchase(psamSlot);
        cardConsumeCalcMac1Params.setIccRand(iccRand);
        cardConsumeCalcMac1Params.setIccPaySerial(iccPaySerial);
        cardConsumeCalcMac1Params.setPayMoney(payMoney);
        cardConsumeCalcMac1Params.setTimeStampNow(timeStampNow);
        cardConsumeCalcMac1Params.setCardInfo(cardInfo);
        //执行计算消费MAC1
        result = cardConsumeCalcMac1(cardConsumeCalcMac1Params, consumeChannel);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "cardConsumeCalcMac1 failed, return code: " + result.getCode());
            return result;
        }
        //rsp data: 4byte psamPaySerial + 4bytes mac1 + 9000
        String psamPaySerial = cardConsumeCalcMac1Params.getOutData().substring(0, 8);
        String mac1 = cardConsumeCalcMac1Params.getOutData().substring(8, 16);
        Log.i(LOG_FLAG, "Calc mac1, psamPaySerial : " + psamPaySerial + "\n" + "mac1 ： " + mac1);
        //保存PSAM卡终端交易序号
        rspTransInfo.setPsamTransSerial(psamPaySerial);
        //构造debitForPurchase接口参数
        DebitForPurchaseParams dbitForPurchaseParams = new DebitForPurchaseParams();
        dbitForPurchaseParams.setConsumeMethod(consumeMethod);
        dbitForPurchaseParams.setChannelType(consumeChannel);
        dbitForPurchaseParams.setChannelID(channelID);
        dbitForPurchaseParams.setTerminalPaySerial(psamPaySerial);
        dbitForPurchaseParams.setDateMac1(timeStampNow + mac1);
        dbitForPurchaseParams.setInData(record0019);
        //执行消费
        result = debitForPurchase(dbitForPurchaseParams);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "debitForPurchase failed, return code: " + result.getCode());
            return result;
        }

        //rsp data: 4bytes tac + 4bytes mac2 + 9000
        String tac = dbitForPurchaseParams.getOutData().substring(0, 8);
        String mac2 = dbitForPurchaseParams.getOutData().substring(8, 16);
        Log.i(LOG_FLAG, "tac : " + tac + "\n" + "mac2 : " + mac2);
        //保存交易识别码
        rspTransInfo.setTac(tac);
        //消费校验MAC2码
        result = cardConsumeVerifyMac2(mac2, psamSlot, consumeChannel);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "cardConsumeVerifyMac2 failed, return code: " + result.getCode());
            return result;
        }
        Log.i(LOG_FLAG, "cardConsumeVerifyMac2 success!");

        //获取消费后的余额,读取卡片0002文件
        int[] newBalance = new int[1];
        result = this.readICCBalance(consumeChannel, channelID, newBalance);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "Read icc balance failed, error code: " + result.getCode());
            return result;
        }
        cardInfo.setBalance(newBalance[0]);
        //保存交易金额和交易后余额
        rspTransInfo.setPayMoney(money);
        rspTransInfo.setCardBalance(newBalance[0]);
        //保存交易类型，标准车道交易类型，取值为0x09（复合消费），其他保留
        rspTransInfo.setTransType((byte) (consumeMethod == GvDevOptConstant.CONSUME_METHOD_COMPLEX ? 9 : 0));

        return result;
    }

    /**
     *
     * @Title debitForPurchaseInit
     * @Description 消费初始化（用于电子存折、电子钱包）
     * @author ZengXiaoJiang
     * @param params 消费初始化参数对象
     * @return GvApiResult 0：成功 | 非0：失败
     */
    private GvApiResult debitForPurchaseInit(DebitForPurchaseInitParams params) throws Exception {
        if(null == params){
            return GvApiResult.API_PARAMS_ERROR;
        }

        String transMoney = GvConvertUtils.bytesToHexString(GvConvertUtils.bigIntToBytes(params.getMoney()), 4);
        Log.i(LOG_FLAG, "transMoney: " + transMoney);

        //进入1001目录
        GvApiResult result = enterDir(params.getChannelType(), params.getChannelID(), "1001");
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "Enter 1001 failed, return code: " + result.getCode());
            return result;
        }

        String[] cosCmds = new String[1];
        int cosNums = 0;

        //消费初始化
        if (params.getConsumeMethod() == GvDevOptConstant.CONSUME_METHOD_COMPLEX) {
            //指令1：复合消费初始化 805003020B01+ 4字节 Money+ 6字节 psamsn
            cosCmds[cosNums++] = String.format("805003%s0B%s%s%s", params.getAppType(), params.getKeyIndex(), transMoney, params.getTerminalNo());
        }else if (params.getConsumeMethod() == GvDevOptConstant.CONSUME_METHOD_SIMPLE) {
            //简单消费： 805001020B01+ 4字节 Money+ 6字节 psamsn
            cosCmds[cosNums++] = String.format("805001%s0B%s%s%s",  params.getAppType(), params.getKeyIndex(), transMoney, params.getTerminalNo());
        }
        //执行指令
        result = this.cosChannel(params.getChannelType(), params.getChannelID(), cosNums, cosCmds);
        if (result == GvApiResult.SUCCESS) {
            int rspCosIndex = cosNums - 1;
            params.setOutData(cosCmds[rspCosIndex].substring(0, cosCmds[rspCosIndex].length()-4));
        }

        return result;
    }

    /**
     * 计算消费MAC1
     * @param params CardConsumeCalcMac1Params参数对象
     * @return GvApiResult : 0(成功) | 其他(失败)
     */
    private GvApiResult cardConsumeCalcMac1(CardConsumeCalcMac1Params params, int consumeChannel) throws Exception {
        if (null == params || null == params.getCardInfo()) {
            return GvApiResult.API_PARAMS_ERROR;
        }

        //指令1： 8070000024 + 4bytes iccRand + 2bytes iccPaySerial + 4bytes payMoney + "09" 计算模式 + 7bytes BCD Timestamp + 1byte KeyVersion + 1byte algorithmId + 8bytes iccardId + 8bytes iccIssureId;
        //指令1返回：4byte psamPaySerial + 4bytes mac1
        String iccardId = params.getCardInfo().getCardId().substring(4);
        Log.i(LOG_FLAG, "Calc mac1, iccardId : " + iccardId);
        String iccIssureId="";
        try {
            Log.i(LOG_FLAG, "cardInfo.getProvider(): " + params.getCardInfo().getProvider());
            byte[] providerBytes = params.getCardInfo().getProvider().getBytes("GBK");
            if (providerBytes != null) {
                iccIssureId = GvConvertUtils.bytesToHexString(providerBytes, 4);
                iccIssureId += iccIssureId;
                Log.i(LOG_FLAG, "Calc mac1, iccIssureId : " + iccIssureId);
            }else{
                Log.e(LOG_FLAG, "Change cardInfo.getProvider() to bytes failed, providerBytes is null");
            }
        } catch (UnsupportedEncodingException e) {
            Log.e(LOG_FLAG, e.getMessage());
        }

        //密钥版本号
        byte keyVersion = 0x01;
        //算法标识
        byte algorithmId = 0x00;

        String[] cosCmds = new String[1];
        int cosNums = 0;
        if (params.getConsumeMethod() == GvDevOptConstant.CONSUME_METHOD_COMPLEX) {
            cosCmds[cosNums++] = String.format("8070000024%s%s%s09%s%02X%02X%s%s",
                    params.getIccRand(),
                    params.getIccPaySerial(),
                    params.getPayMoney(),
                    params.getTimeStampNow(),
                    keyVersion,
                    algorithmId,
                    iccardId,
                    iccIssureId);
        }else if (params.getConsumeMethod() == GvDevOptConstant.CONSUME_METHOD_SIMPLE) {
            cosCmds[cosNums++] = String.format("8070000024%s%s%s06%s%02X%02X%s%s",
                    params.getIccRand(),
                    params.getIccPaySerial(),
                    params.getPayMoney(),
                    params.getTimeStampNow(),
                    keyVersion,
                    algorithmId,
                    iccardId,
                    iccIssureId);
        }

        GvApiResult result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, params.getPsamSlotForPurchase(), cosNums, cosCmds);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "cardConsumeCalcMac1 failed. error code : " + result.getCode());
            return result;
        }
        params.setOutData(cosCmds[0]);
        return result;
    }

    /**
     *
     * @Title debitForPurchase
     * @Description 消费
     * @author ZengXiaoJiang
     * @param params DebitForPurchaseParams参数对象
     * @return GvApiResult 0：成功 | 非0：失败
     */
    private GvApiResult debitForPurchase(DebitForPurchaseParams params) throws Exception {
        if (null == params) {
            return GvApiResult.API_PARAMS_ERROR;
        }

        String[] cosCmds = new String[1];
        int cosNums = 0;

        GvApiResult result;
        if (params.getConsumeMethod() == GvDevOptConstant.CONSUME_METHOD_COMPLEX) {
            if (params.getInData() == null || params.getInData().trim().equals("")) {
                return GvApiResult.API_PARAMS_ERROR;
            }
            //复合消费+写0019文件
            //指令1：复合写0019：80DCAAC8+len+data ,AA2900固定3字节
            cosCmds[cosNums++] = String.format("80DCAAC82BAA2900%s", params.getInData().substring(6));
            //执行指令
            result = this.cosChannel(params.getChannelType(), params.getChannelID(), cosNums, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                Log.e(LOG_FLAG, "Write 0019 failed. error code : " + result.getCode());
                return result;
            }
        }

        cosNums = 0;
        //指令：执行消费 805401000F + 4bytes psamPaySerial + 7bytes timestamp + 4bytes MAC1
        cosCmds[cosNums++] = String.format("805401000F%s%s", params.getTerminalPaySerial(), params.getDateMac1());
        //执行指令
        result = this.cosChannel(params.getChannelType(), params.getChannelID(), cosNums, cosCmds);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "Consume failed. error code : " + result);
        } else {
            //rsp data: 4bytes tac + 4bytes mac2 + 9000
            int rspCosIndex = cosNums - 1;
            params.setOutData(cosCmds[rspCosIndex].substring(0, cosCmds[rspCosIndex].length() - 4));
        }

        return result;
    }

    /**
     * 验证消费MAC2码
     * @param mac2 执行消费指令后返回的MAC码
     * @param psamSlotForPurchase 消费psam卡槽号
     * @return GvApiResult : 0(成功) | 其他(失败)
     */
    private GvApiResult cardConsumeVerifyMac2(String mac2, int psamSlotForPurchase, int consumeChannel) throws Exception {
        //消费校验MAC2码
        //指令1:8072000004 + 4bytes mac2
        // 测试的时候，不做MAC2校验，以防锁卡
        String[] cosCmds = new String[1];
        int cosNums = 0;
        cosCmds[cosNums++] = String.format("8072000004%s", mac2);
        GvApiResult result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, psamSlotForPurchase, cosNums, cosCmds);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "cardConsumeVerifyMac2 failed. error code : " + result.getCode());
        } else {
            Log.i(LOG_FLAG, "cardConsumeVerifyMac2 success.");
        }
        return result;
    }

    /**
     * 执行本地PSAM解密车辆信息
     * @param psamSlot 解密车辆信息的PSAM卡槽
     * @param psamDir 解密车辆信息的PSAM目录，例如DF01
     * @param psamKeyId 解密车辆信息的密钥索引，例如5903
     * @param vehInfoLen 读取的车辆信息明文长度，最长79字节
     * @param random 8字节随机数
     * @param obuVehInfo 车辆信息对象
     * @return GvApiResult : 0成功，非0失败
     * @throws Exception
     */
    private GvApiResult doLocalDecodeVehInfo(
            int psamSlot,
            String psamDir,
            String psamKeyId,
            int vehInfoLen,
            String random,
            String divData,
            ObuVehInfo obuVehInfo) throws Exception{
        Log.i(LOG_FLAG, "Start read vehicle information.");

        //读取车辆信息密文
        String[] vehEncode = new String[1];
        GvApiResult result = this.getVehEncodeByCosCmd(random, vehEncode);
        if(result != GvApiResult.SUCCESS){
            return result;
        }
        Log.i(LOG_FLAG, "vehEncode length: " + (vehEncode[0].length() >> 1) + "bytes, data: " + vehEncode[0]);

        //解密车辆信息
        //enter psam directory
        result = this.enterDir(GvDevOptConstant.CHANNEL_TYPE_DSRC, psamSlot, psamDir);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG,  "Enter psam directory DF01 failed." );
            return result;
        }
        Log.i(LOG_FLAG,  "Enter psam directory DF01 success." );

        //分散密钥
        result = this.localPsamDivKey(psamSlot, psamKeyId, divData);
        if(result != GvApiResult.SUCCESS){
            Log.e(LOG_FLAG, "localPsamDivKey return failed. error code : " + result.getCode());
            return result;
        }

        //车辆信息明文，esam返回72字节，格式为：有效字节长度+8字节mac+59字节车辆信息+补充的无效字节+9000
        String[] vehDecode = new String[1];
        result = this.localPsamDecode(psamSlot, vehEncode[0], vehDecode);
        if(result != GvApiResult.SUCCESS){
            Log.e(LOG_FLAG,  "localPsamDecode return failed. error code : " + result.getCode());
            return result;
        }

        //解密车辆信息得到的8字节mac
        String mac1 = vehDecode[0].substring(2, 18);
        Log.i(LOG_FLAG,  "MAC1 : "+ mac1);

        //车辆信息明文
        String vehInfoPlaintext = vehDecode[0].substring(18, vehInfoLen*2 + 18);
        Log.i(LOG_FLAG,  "vehInfoPlaintext : " + vehInfoPlaintext);

        //分散密钥
        result = this.localPsamDivKey(psamSlot, psamKeyId, divData);
        if(result != GvApiResult.SUCCESS){
            Log.e(LOG_FLAG,  "localPsamDivKey return failed. error code : " + result.getCode());
            return result;
        }

        //使用车辆信息明文计算的8字节mac
        String[] mac2 = new String[1];
        result = this.localPsamCalcMAC(psamSlot, random, vehInfoPlaintext, 8, mac2);
        if(result != GvApiResult.SUCCESS){
            Log.e(LOG_FLAG,  "localPsamCalcMAC8 return failed. error code : " + result.getCode());
            return result;
        }
        Log.i(LOG_FLAG,  "MAC2 : "+ mac2[0]);

        //比较MAC1和MAC2
        if(!mac1.equals(mac2[0])){
            Log.e(LOG_FLAG, "Check mac error.");
            return GvApiResult.READ_VEH_CHECK_MAC_FAILED;
        }

        //解析车辆信息
        result = this.parseVehicleInfo(GvConvertUtils.hexStringToBytes(vehInfoPlaintext), obuVehInfo);
        if( result != GvApiResult.SUCCESS ){
            Log.e(LOG_FLAG, "Parse vehicle information failed.");
            return result;
        }

        //打印车辆信息
        StringBuilder sb = new StringBuilder();
        if (obuVehInfo.getMyVehType() == 0) {
            sb.append("Vehicle plate number:").append(obuVehInfo.getLicencePlateNum()).append("\r\n")
                    .append("Vehicle plate color: ").append(obuVehInfo.getLicencePlateColor()).append("\r\n")
                    .append("Vehicle type: ").append(obuVehInfo.getVehicleType()).append("\r\n")
                    .append("Vehicle user type: ").append(obuVehInfo.getVehicleUserType()).append("\r\n")
                    .append("Vehicle length: ").append(obuVehInfo.getVehicleLength()).append("\r\n")
                    .append("Vehicle width: ").append(obuVehInfo.getVehicleWidth()).append("\r\n")
                    .append("Vehicle height: ").append(obuVehInfo.getVehicleLength()).append("\r\n")
                    .append("Vehicle wheels: ").append(obuVehInfo.getVehicleHeight()).append("\r\n")
                    .append("Vehicle wheelbase: ").append(obuVehInfo.getVehicleWheels()).append("\r\n")
                    .append("Vehicle weight or seat limits: ").append(obuVehInfo.getWeitghtOrSeatLimits()).append("\r\n")
                    .append("Vehicle description: ").append(obuVehInfo.getVehicleDescription()).append("\r\n")
                    .append("Vehicle engine number: ").append(obuVehInfo.getVehicleEngineNumber());
        } else {
            sb.append("Vehicle plate number:").append(obuVehInfo.getLicencePlateNum()).append("\r\n")
                    .append("Vehicle plate color: ").append(obuVehInfo.getLicencePlateColor()).append("\r\n")
                    .append("Vehicle type: ").append(obuVehInfo.getVehicleType()).append("\r\n")
                    .append("Vehicle user type: ").append(obuVehInfo.getVehicleUserType()).append("\r\n")
                    .append("Vehicle length: ").append(obuVehInfo.getVehicleLength()).append("\r\n")
                    .append("Vehicle width: ").append(obuVehInfo.getVehicleWidth()).append("\r\n")
                    .append("Vehicle height: ").append(obuVehInfo.getVehicleLength()).append("\r\n")
                    .append("Vehicle vehicleWeightLimits: ").append(obuVehInfo.getVehicleWeightLimits()).append("\r\n")
                    .append("Vehicle curbWeight: ").append(obuVehInfo.getCurbWeight()).append("\r\n")
                    .append("Vehicle totalMass: ").append(obuVehInfo.getTotalMass()).append("\r\n")
                    .append("Vehicle seatLimits: ").append(obuVehInfo.getSeatLimits()).append("\r\n")
                    .append("Vehicle vin: ").append(obuVehInfo.getVin()).append("\r\n")
                    .append("Vehicle description: ").append(obuVehInfo.getVehicleDescription());
        }
        Log.i(LOG_FLAG, sb.toString());

        return result;
    }

    /**
     * 更新卡片0015文件
     * @param psamSlot 计算写标签信息MAC的psam卡槽号，如2
     * @param psamDir 计算写标签信息MAC的psam目录，如"DF01"
     * @param psamKeyId 计算写标签系统信息MAC的密钥索引，如"4801"
     * @param cardInfo 卡片0015文件信息
     * @param cardOwner 卡片0016文件信息
     * @return GvApiResult : 0(成功) | 其他(失败)
     * @throws Exception
     */
    private GvApiResult doLocalSecIssueCard(
            int psamSlot,
            String psamDir,
            String psamKeyId,
            CardInformation cardInfo,
            CardOwner cardOwner) throws Exception {
        if (TextUtils.isEmpty(psamDir)
                || TextUtils.isEmpty(psamKeyId)
                || cardInfo == null
                || cardOwner == null) {
            return GvApiResult.API_PARAMS_ERROR;
        }

        //检测卡片
        CardInformation tmpCardInfo = new CardInformation();
        int ret = sdk.getCardInformation(tmpCardInfo);
        if (ret != 0) {
            return GvApiResult.CARD_NO_FIND;
        }

        tmpCardInfo.setSignedDate(cardInfo.getSignedDate());
        tmpCardInfo.setExpiredDate(cardInfo.getExpiredDate());
        tmpCardInfo.setVehicleNumber(cardInfo.getVehicleNumber());

        tmpCardInfo.setUserType(cardInfo.getUserType());
        tmpCardInfo.setPlateColor(cardInfo.getPlateColor());
        tmpCardInfo.setVehicleModel(cardInfo.getVehicleModel());

        //分散因子
        byte[] cardProviderBytes = GvConvertUtils.stringToByte(tmpCardInfo.getProvider());
        String cardProvider = GvConvertUtils.bytesToHexString(cardProviderBytes, cardProviderBytes.length);
        Log.i(LOG_FLAG, "Card provider: " + cardProvider);
        String divData = tmpCardInfo.getCardId().substring(4)
                + cardProvider.substring(0,8)
                + cardProvider.substring(0,8);

        //更新卡片0015文件
        GvApiResult result = this.updateCardFile0015(
                psamSlot,
                psamDir,
                psamKeyId,
                divData,
                tmpCardInfo);
        if ( result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "updateCardFile0015 failed, return code: " + result.getCode());
            return result;
        }
        Log.i(LOG_FLAG, "更新卡片0015文件成功");

        //更新卡片0016文件
        result = this.updateCardFile0016(
                psamSlot,
                psamDir,
                psamKeyId,
                divData,
                cardOwner);
        if ( result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "updateCardFile0016 failed, return code: " + result.getCode());
        }
        Log.i(LOG_FLAG, "更新卡片0016文件成功");

        return result;
    }

    public GvApiResult sign(PsamInfo psamInfo) throws Exception {
        //关闭dsrc
//        this.closeDsrc();
        //打开dsrc
        GvApiResult result = GvApiResult.SUCCESS;
//        if (!isOpenDsrc) {
//            result = this.openDsrc();//开天线
//        }
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }
        //执行业务
        result = this.doSign(psamInfo);
        //关闭dsrc
//        this.closeDsrc();
        return result;
    }

    private GvApiResult doSign(PsamInfo psamInfo){
        try {

            //读取3F00目录下0015系统信息文件
            String[] cosCmds2 = new String[1];
            cosCmds2[0] = "00B095000E";
            GvApiResult result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, cardSlot, 1, cosCmds2);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            psamInfo.setXulie(cosCmds2[0].substring(0,20));
            cosCmds2[0] = "00B0960006";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, cardSlot, 1, cosCmds2);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            psamInfo.setTerminalNo(cosCmds2[0].substring(0,12));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return GvApiResult.SUCCESS;
    }

    public GvApiResult auth(PsamInfo psamInfo) throws Exception {
//        //关闭dsrc
//        this.closeDsrc();
//        try {
//            Thread.sleep(300);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        //打开dsrc
        GvApiResult result = GvApiResult.SUCCESS;
//        if (!isOpenDsrc) {
//            result = this.openDsrc();//开天线
//        }
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }
        //执行业务
        result = this.doAuth(psamInfo);
        //关闭dsrc
//        this.closeDsrc();
        return result;
    }
    private GvApiResult doAuth(PsamInfo psamInfo){
        try {
            LogUtil.i("sdkhelper", "复位psam卡槽："+cardSlot);
            writeLog("复位PSAM卡槽：" + cardSlot);
            //复位psam
            int ret = sdk.moduleReset((byte) cardSlot);
            if (ret != 0) {
                return GvApiResult.CARD_RESET_FAILED;
            }
            //读取3F00目录下0015系统信息文件
            String[] cosCmds2 = new String[1];
            cosCmds2[0] = "00B095000E";
            GvApiResult result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, cardSlot, 1, cosCmds2);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //获取8字节随机数
            String[] random = new String[1];
            result = this.getChallenge(GvDevOptConstant.CHANNEL_TYPE_DSRC, cardSlot, (byte) 4, random);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            random[0] += "000000000000000000000000";

            psamInfo.setXulie(cosCmds2[0].substring(0,20));
            psamInfo.setBanben(cosCmds2[0].substring(20,22));
            psamInfo.setSuijishu(random[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return GvApiResult.SUCCESS;
    }
    //无卡写obu ef04
    public GvApiResult OBUNoCard(byte[] bytes, byte[] bytes2){
        try {
            GvApiResult result = enterDir(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, "DF01");
            if (result != GvApiResult.SUCCESS) {
                sdk.obuRelease(result == GvApiResult.SUCCESS, true);
//                closeDsrc();
                return result;
            }
            //选择obu ef04文件
            String[] cosCmds = new String[1];
            cosCmds[0] = "00A4000002EF04";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //ef04 315-317字节写入“0xBB 0x29 0x00”
            cosCmds[0] = "00D6013A03"+ MyUtil.bytesToHexString(bytes, bytes.length);
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //ef04 354-409字节填充0x00 方案到405错了
            cosCmds[0] = "00D6016138"+ MyUtil.bytesToHexString(bytes2, bytes2.length);
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return GvApiResult.SUCCESS;
    }
    //卡余额0 写obu ef04
    public GvApiResult cardNoCash(String result0019, String result0015){
        try {
            //选择obu ef04文件
            String[] cosCmds = new String[1];
            cosCmds[0] = "00A4000002EF04";
            GvApiResult result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //ef04 315-317字节写入“0xAA 0x29 0x00”
//            cosCmds[0] = "00D6013A03AA2900";
//            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
//            if (result != GvApiResult.SUCCESS) {
//                return result;
//            }
            //第315-353字节用户卡0019文件第1-39字节
            cosCmds[0] = "00D6013A27"+ result0019;
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //354-373字节写入用户卡0015文件第1-20字节
            cosCmds[0] = "00D6016114"+ result0015.substring(0,20);
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //374字节写入0015文件第41字节（车辆用户类型）
            cosCmds[0] = "00D6017501"+ result0015.substring(40,42);
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //ef04 375-409字节填入0x00
            byte[] bytes2 = new byte[35];
            for (int i = 0; i < bytes2.length; i++){
                bytes2[i] = 0x00;
            }
            cosCmds[0] = "00D6017623"+ MyUtil.bytesToHexString(bytes2, bytes2.length);
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return GvApiResult.SUCCESS;
    }

    public GvApiResult clearOBUEF04(){
        try {
            GvApiResult result = enterDir(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, "DF01");
            if (result != GvApiResult.SUCCESS) {
                sdk.obuRelease(result == GvApiResult.SUCCESS, true);
//                closeDsrc();
                return result;
            }
            //选择obu ef04文件
            String[] cosCmds = new String[1];
            cosCmds[0] = "00A4000002EF04";
            LogUtil.i("PosEntryChargeActivity", "00A4000002EF04");
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }

            //80个字节写入0x00
            byte[] bytes = new byte[80];
            for (int i = 0; i < bytes.length; i++){
                bytes[i] = 0x00;
            }
            //第315-317字节写入0x00
            cosCmds[0] = "00D6013A03000000";
            LogUtil.i("PosEntryChargeActivity", "00D6013B03000000");
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //第318-397字节写入0x00
            cosCmds[0] = "00D6013D50"+ MyUtil.bytesToHexString(bytes, bytes.length);
            LogUtil.i("PosEntryChargeActivity", "00D6019680");
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //第406-485字节写入0x00
            cosCmds[0] = "00D6019550"+ MyUtil.bytesToHexString(bytes, bytes.length);
            LogUtil.i("PosEntryChargeActivity", "00D6019680");
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return GvApiResult.SUCCESS;
    }
    public GvApiResult writeOBUEF04(String result0019, String result0015, String realFee,String payFee, String mileage){
        try {
//            GvApiResult result = enterDir(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, "DF01");
//            if (result != GvApiResult.SUCCESS) {
//                sdk.obuRelease(result == GvApiResult.SUCCESS, true);
//                closeDsrc();
//                return result;
//            }
            //选择obu ef04文件
            String[] cosCmds = new String[1];
            cosCmds[0] = "00A4000002EF04";
            LogUtil.i("PosEntryChargeActivity", "00A4000002EF04");
            GvApiResult result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //第315-353字节用户卡0019文件第1-39字节
            cosCmds[0] = "00D6013A27"+ result0019;
            LogUtil.i("PosEntryChargeActivity", "00D6013B27");
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //354-373字节写入用户卡0015文件第1-20字节
            cosCmds[0] = "00D6016114"+ result0015.substring(0,40);
            LogUtil.i("PosEntryChargeActivity", "00D6016214"+ result0015.substring(0,40));
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //374字节写入0015文件第41字节（车辆用户类型）
            cosCmds[0] = "00D6017501"+ result0015.substring(80,82);
            LogUtil.i("PosEntryChargeActivity", "00D6017601"+ result0015.substring(40,42));
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //第375字节（通行省份个数）填写为0x01
            cosCmds[0] = "00D601760101";
            LogUtil.i("PosEntryChargeActivity", "00D601770101");
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //第376-387字节填入0x00
            byte[] bytes = new byte[12];
            for (int i = 0; i < bytes.length; i++){
                bytes[i] = 0x00;
            }
            cosCmds[0] = "00D601770C"+ MyUtil.bytesToHexString(bytes, bytes.length);
            LogUtil.i("PosEntryChargeActivity", "00D601780C");
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //第388-390字节填入本入口收费站编码（HEX码）
            cosCmds[0] = "00D6018303000000";
            LogUtil.i("PosEntryChargeActivity", "00D6018303000000");
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //第391-409字节填写0x00
            byte[] bytes2 = new byte[19];
            for (int i = 0; i < bytes2.length; i++){
                bytes2[i] = 0x01;
            }
            cosCmds[0] = "00D6018613"+ MyUtil.bytesToHexString(bytes2, bytes2.length);
            LogUtil.i("PosEntryChargeActivity", "00D6018713");
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }

            //兼具门架功能的收费站入口 需要写以下数据
            // 第382-383字节填写0x0001
            cosCmds[0] = "00D6017D020001";
            LogUtil.i("PosEntryChargeActivity", "00D6017E020001");
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //第394字节填写0x01
            cosCmds[0] = "00D601890101";
            LogUtil.i("PosEntryChargeActivity", "00D6018A0101");
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //第376-378字节和第379-381字节分别填写代收门架的应收金额和实收金额
            cosCmds[0] = "00D6017706"+payFee+realFee;
            LogUtil.i("PosEntryChargeActivity", "00D6017806"+payFee+realFee);
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //第384-386字节填写代收门架的累计计费里程
            cosCmds[0] = "00D6017F03"+mileage;
            LogUtil.i("PosEntryChargeActivity", "00D6017F03"+mileage);
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //第391-393字节填写代收门架应收金额
            cosCmds[0] = "00D6018603"+payFee;
            LogUtil.i("PosEntryChargeActivity", "00D6018603"+payFee);
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //第395-397字节填写代收门架实收金额
            cosCmds[0] = "00D6018A03"+realFee;
            LogUtil.i("PosEntryChargeActivity", "00D6018A03"+realFee);
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }

            //80个字节写入0x00
            byte[] bytes3 = new byte[80];
            for (int i = 0; i < bytes3.length; i++){
                bytes3[i] = 0x00;
            }
            //第406-485字节写入0x00
            cosCmds[0] = "00D6019550"+ MyUtil.bytesToHexString(bytes3, bytes3.length);
            LogUtil.i("PosEntryChargeActivity", "00D6019680");
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return GvApiResult.SUCCESS;
    }

    public GvApiResult readOBUEF04(OBUEF04MDL obuEF04){
        try {
            GvApiResult result = enterDir(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, "DF01");
            if (result != GvApiResult.SUCCESS) {
                sdk.obuRelease(result == GvApiResult.SUCCESS, true);
//                closeDsrc();
                return result;
            }
            //选择obu ef04文件
            String[] cosCmds = new String[1];
            cosCmds[0] = "00A4000002EF04";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            String ef04 = "";
            //读ef04 315-405字节
            cosCmds[0] = "00B0013A5B";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            ef04 = cosCmds[0].substring(0,cosCmds[0].length()-4);
            //读ef04 406-485字节
            cosCmds[0] = "00B0019550";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            obuEF04.setObuEF04(ef04+cosCmds[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return GvApiResult.SUCCESS;
    }



    public GvApiResult card0019Cash(String new0019){
        try {
            String[] cosCmds = new String[1];
            //选择卡 1001目录
            cosCmds[0] = "00A40000021001";
            GvApiResult result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ICC, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //40-43交易累计金额
            cosCmds[0] = "80DCAAC82B"+new0019;
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ICC, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //选择obu ef04文件
            cosCmds[0] = "00A4000002EF04";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //第315-317字节
            cosCmds[0] = "00D6013B03000000";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ESAM, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return GvApiResult.SUCCESS;
    }
    /*public GvApiResult readETC0019(){
        try {
            //卡 1001目录
            String[] cosCmds = new String[1];
            //选择0019
            cosCmds[0] = "00A40000020019";
            GvApiResult result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ICC, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //读0019 1-43字节
            cosCmds[0] = "00B201CC2B";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_OBU, GvDevOptConstant.OBU_CHANNEL_ICC, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return GvApiResult.SUCCESS;
    }*/
    public GvApiResult empower(PsamInfo psamInfo) throws Exception {
        //打开dsrc
        GvApiResult result = GvApiResult.SUCCESS;
//        if (!isOpenDsrc) {
//            result = this.openDsrc();//开天线
//        }
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }
        //执行业务
        result = this.doEmpower(psamInfo);
        //关闭dsrc
//        this.closeDsrc();
        return result;
    }

    public GvApiResult writeCpcCode(CpcEntryMDL cpcEntryMDL) throws Exception {
        //打开dsrc
        GvApiResult result;
        //        = this.openDsrc();
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }
        //执行业务
        result = this.doWriteCpcCode(cpcEntryMDL);
        //关闭dsrc
//        this.closeDsrc();
        return result;
    }
    //入口写带ef02 和ef04
    public GvApiResult writeCpcInfo(int area, CpcEntryExitInfo cpcEntryExitInfo, String ef02, String ef04) throws Exception {
        //打开dsrc
        GvApiResult result = GvApiResult.SUCCESS;
//        if (!isOpenDsrc) {
//            result = this.openDsrc();//开天线
//        }
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }
        //执行业务
        result = this.doWriteCpcInfo(area, cpcEntryExitInfo, ef02, ef04);
        //关闭dsrc
//        this.closeDsrc();
        return result;
    }
    public GvApiResult writeCpcInfo(int area, CpcEntryExitInfo cpcEntryExitInfo) throws Exception {
        //打开dsrc
        GvApiResult result = GvApiResult.SUCCESS;
//        if (!isOpenDsrc) {
//            result = this.openDsrc();//开天线
//        }
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }
        //执行业务
        result = this.doWriteCpcInfo(area, cpcEntryExitInfo, "" ,"");
        //关闭dsrc
//        this.closeDsrc();
        return result;
    }
    private GvApiResult doWriteCpcInfo(int area, CpcEntryExitInfo cpcEntryExitInfo, String ef02, String ef04){
        try {
            //复位cpc
            int ret = sdk.moduleReset((byte) 0);
            if (ret != 0) {
//                this.closeDsrc();
                return GvApiResult.CARD_RESET_FAILED;
            }
            //进入cpc卡3F00目录
            GvApiResult result = this.enterDir(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, "3F00");
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            cpcSysInfo = new CpcSysInfoMDL();
            //读CPC基本信息文件MF/EF02（CPC卡）
            String[] cosCmdsoo = new String[1];
            cosCmdsoo[0] = "00B0820040";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmdsoo);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //设置EF02电量及5.8g开关信息
            cpcSysInfo.setBattry(cosCmdsoo[0].substring(0, 2));
            cpcSysInfo.setIs58Open(cosCmdsoo[0].substring(2, 4));
            int num = Integer.parseInt(cpcSysInfo.getBattry(),16);//十六进制转十进制
            String binary = Integer.toBinaryString(num);//十进制转2进制
            if(binary.length()==8&&binary.startsWith("1")){//8位且最高位是1
                return result.CPC_BATTRY_LOW;
            }

            //读取系统文件MF/EF01（CPC卡）
            String[] cosCmdsoo1 = new String[1];
            cosCmdsoo1[0] = "00B081001E";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmdsoo1);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //设置EF01系统信息
            cpcSysInfo.setCpcInfo(cosCmdsoo[0]);
            quyudaima = cosCmdsoo1[0].substring(0,8);
            kahao = cosCmdsoo1[0].substring(16,32);

            //选择DF01应用（PSAM卡）
            String[] cosCmds = new String[1];
            cosCmds[0] = "00A4000002DF01";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, cardSlot, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //将省级行政区域代码、
            //卡号发给 PSAM 分散密
            //钥（PSAM卡）
            String[] cosCmds1 = new String[1];
            cosCmds1[0] = "801A484410"+kahao+quyudaima+quyudaima;
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, cardSlot, 1, cosCmds1);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //选择DF01应用（CPC卡）
            String[] cosCmds2 = new String[1];
            cosCmds2[0] = "00A4000002DF01";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmds2);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //获取8字节随机数
            String[] random = new String[1];
            result = this.getChallenge(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, (byte) 8, random);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            random[0] += "0000000000000000";
            //PSAM卡加密随机数（PSAM卡）
            String[] cosCmds3 = new String[1];
            cosCmds3[0] = "80FA000010"+random[0];
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, cardSlot, 1, cosCmds3);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            byte[] authData = new byte[8];
            byte[] outDataByte = GvConvertUtils.hexStringToBytes(cosCmds3[0]);
            //将加密后的16字节密文前后8字节进行异或
            for (int i = 0; i < 8; i++) {
                authData[i] = (byte) (outDataByte[i] ^ outDataByte[i + 8]);
            }
            //执行外部认证
            String[] cosCmds4 = new String[1];
            cosCmds4[0] = "0082000108"+GvConvertUtils.bytesToHexString(authData, authData.length);
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmds4);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //写出入口文件
            String[] cosCmds5 = new String[1];
            if(area == 1)//1写入1
                cosCmds5[0] = "00D6810080" + cpcEntryExitInfo.getHexEF01Info();
            else if(area == 2)//2状态恢复2
                cosCmds5[0] = "00D68117020202" ;
            result = cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmds5);
            if (result != GvApiResult.SUCCESS) {
                Log.e(LOG_FLAG, "写出口文件失败");
                return result;
            }
            //是入口，则清除路径文件
            //清除过站信息
            result = this.clearCpcFile(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, "EF02");
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            if (area == 1&&!TextUtils.isEmpty(ef02)){
                //80个字节写入0x00
                byte[] bytes = new byte[128];
                for (int i = 0; i < bytes.length; i++){
                    bytes[i] = (byte) 0xFF;
                }
                byte[] bytes2 = new byte[27];
                for (int i = 0; i < bytes2.length; i++){
                    bytes2[i] = (byte) 0xFF;
                }
                String[] coswriteef02 = new String[1];
                String lenth = Integer.toHexString(ef02.length()/2);
                coswriteef02[0] = "00D68200"+lenth+ef02;
                result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, coswriteef02);
                if (result != GvApiResult.SUCCESS) {
                    return result;
                }
                //101-209 FF
                cosCmds[0] = "00D6006580"+MyUtil.bytesToHexString(bytes, bytes.length);
                result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmds);
                if (result != GvApiResult.SUCCESS) {
                    return result;
                }
                //209-357 FF
                cosCmds[0] = "00D600E580"+MyUtil.bytesToHexString(bytes, bytes.length);
                result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmds);
                if (result != GvApiResult.SUCCESS) {
                    return result;
                }
                //357-485 FF
                cosCmds[0] = "00D6016580"+MyUtil.bytesToHexString(bytes, bytes.length);
                result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmds);
                if (result != GvApiResult.SUCCESS) {
                    return result;
                }
                //485-512 FF
                cosCmds[0] = "00D601E51B"+MyUtil.bytesToHexString(bytes2, bytes2.length);
                result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmds);
                if (result != GvApiResult.SUCCESS) {
                    return result;
                }
            }
            //清除计费信息
            result = this.clearCpcFile(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, "EF04");
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            if (area == 1&&!TextUtils.isEmpty(ef04)){
                String[] coswriteef04 = new String[1];
                coswriteef04[0] = "00D6840017"+ef04;
                result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, coswriteef04);
                if (result != GvApiResult.SUCCESS) {
                    return result;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return GvApiResult.SUCCESS;
    }

    public GvApiResult decipheringOBU(String serialNumber, String provider,ObuVehInfo obuVehInfo ) throws Exception {
        //打开dsrc
        GvApiResult result = GvApiResult.SUCCESS;
//        if (!isOpenDsrc) {
//            result = this.openDsrc();//开天线
//        }
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }
        //执行业务
        result = this.doDecipheringOBU(serialNumber, provider, obuVehInfo );
        //关闭dsrc
//        this.closeDsrc();
        return result;
    }
    private GvApiResult doDecipheringOBU(String serialNumber, String provider, ObuVehInfo obuVehInfo){
        try {
            writeLog("解密车辆信息");
            //选择DF01应用（PSAM卡）
            String[] cosCmds = new String[1];
            cosCmds[0] = "00A4000002DF01";
            GvApiResult result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, cardSlot, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                writeLog("解密车辆信息失败1："+result.getMsg());
                return result;
            }

            //读取车辆信息密文
            String[] vehEncode = new String[1];
            result = this.getVehEncodeByCosCmd("0000000000000000", vehEncode);
            if(result != GvApiResult.SUCCESS){
                writeLog("解密车辆信息失败2："+result.getMsg());
                return result;
            }
            Log.i(LOG_FLAG, "vehEncode length: " + (vehEncode[0].length() >> 1) + "bytes, data: " + vehEncode[0]);

//            vehEncode[0] = "58C9F592A836FEC1A172ECBB573D1168FD44E4AFE569CB60FD853DC5DBAEC20A547139C1A8A2547C33C839EE98D279BB415FD6D59522F44D5A399545A202AEC133C839EE98D279BB33C839EE98D279BB33C839EE98D279BB";
            //PSAM解密车辆信息
            //分散密钥
            result = this.localPsamDivKey(cardSlot, "5903", serialNumber+provider+provider);
            if(result != GvApiResult.SUCCESS){
                Log.e(LOG_FLAG, "localPsamDivKey return failed. error code : " + result.getCode());
                writeLog("解密车辆信息失败3："+result.getMsg());
                return result;
            }
            //车辆信息明文，格式为：有效字节长度+8字节或16字节mac+79字节车辆信息+补充的无效字节+9000
            String[] vehDecode = new String[1];
            result = this.localPsamDecode(cardSlot, vehEncode[0], vehDecode);
            if(result != GvApiResult.SUCCESS){
                writeLog("解密车辆信息失败4："+result.getMsg());
                Log.e(LOG_FLAG,  "localPsamDecode return failed. error code : " + result.getCode());
                return result;
            }


            //解密车辆信息得到的8字节或16字节mac
            String mac1;
            //车辆信息明文
            String vehInfoPlaintext = "";

            mac1 = vehDecode[0].substring(2, 18);
            Log.i(LOG_FLAG, "MAC1 : " + mac1);
            //车辆信息明文
            vehInfoPlaintext = vehDecode[0].substring(18, 79*2 + 18);
            Log.i(LOG_FLAG,  "vehInfoPlaintext : " + vehInfoPlaintext);

            obuVehInfo.setVehicleInfoPlaintext(vehInfoPlaintext);
            return GvApiResult.SUCCESS;
            //解析车辆信息
            /*result = this.parseVehicleInfo(GvConvertUtils.hexStringToBytes(vehInfoPlaintext), obuVehInfo);
            if( result != GvApiResult.SUCCESS ){
                Log.e(LOG_FLAG, "Parse vehicle information failed.");
                return result;
            }*/


        } catch (Exception e) {
            e.printStackTrace();
        }
        return GvApiResult.SUCCESS;
    }

    private GvApiResult doWriteCpcCode(CpcEntryMDL cpcEntryMDL){
        try {
            //发送PSAM授权指令给PSAM卡（PSAM卡）
            String[] cosCmds = new String[1];
            cosCmds[0] = cpcEntryMDL.getAuthInstruction();
            GvApiResult result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, cardSlot, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            cpcEntryMDL.setResult(cosCmds[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return GvApiResult.SUCCESS;
    }

    public String getKahao(){
        return kahao;
    }

    public String getQuyudaima(){
        return quyudaima;
    }

    public CpcSysInfoMDL getCpcSysInfo(){
        return cpcSysInfo;
    }

    public GvApiResult findCPC() throws Exception {
        //打开dsrc
        GvApiResult result = GvApiResult.SUCCESS;
//        if (!isOpenDsrc) {
//            result = this.openDsrc();//开天线
//        }
//        if (result != GvApiResult.SUCCESS) {
//            return result;
//        }
        //执行业务
        result = this.fuweiCPC();
        //关闭dsrc
//        this.closeDsrc();
        return result;
    }
    private GvApiResult fuweiCPC(){
        //复位cpc
        int ret = sdk.moduleReset((byte) 0);
        if (ret != 0) {
            return GvApiResult.ETC_MODULE_RESET_FAILED;
        }
        return GvApiResult.SUCCESS;
    }

    private GvApiResult doEmpower(PsamInfo psamInfo){
        try {
            //复位cpc
            int ret = sdk.moduleReset((byte) 0);
            if (ret != 0) {
                return GvApiResult.ETC_MODULE_RESET_FAILED;
            }
            //进入cpc卡3F00目录
            GvApiResult result = this.enterDir(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, "3F00");
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //读CPC基本信息文件MF/EF02（CPC卡）
            String[] cosCmds = new String[1];
            cosCmds[0] = "00B0820040";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //读取系统文件MF/EF01（CPC卡）
            String[] cosCmds1 = new String[1];
            cosCmds1[0] = "00B081001E";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmds1);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //读取3F00目录下0015系统信息文件
            String[] cosCmds2 = new String[1];
            cosCmds2[0] = "00B095000E";
            result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, 1, 1, cosCmds2);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            //获取8字节随机数
            String[] random = new String[1];
            result = this.getChallenge(GvDevOptConstant.CHANNEL_TYPE_DSRC, 1, (byte) 4, random);
            if (result != GvApiResult.SUCCESS) {
                return result;
            }
            random[0] += "000000000000000000000000";

            psamInfo.setDianliang(cosCmds[0].substring(0,2));
            psamInfo.setShengdaima(cosCmds1[0].substring(0,8));
            quyudaima = cosCmds1[0].substring(0,8);
            psamInfo.setCardno(cosCmds1[0].substring(16,32));
            kahao = cosCmds1[0].substring(16,32);
            psamInfo.setXulie(cosCmds2[0].substring(0,20));
            psamInfo.setBanben(cosCmds2[0].substring(20,22));
            psamInfo.setSuijishu(random[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return GvApiResult.SUCCESS;
    }
    /**
     * 执行写CPC卡出入口信息
     * @param area 区域
     * @param cpcEntryExitInfo cpc出入口信息
     * @return GvApiResult: 0成功，非0失败
     * @throws Exception
     */
    private GvApiResult doWriteCpcEntryExitInfo(int area, CpcEntryExitInfo cpcEntryExitInfo) throws Exception {
        //复位cpc
        int ret = sdk.moduleReset((byte) 0);
        if (ret != 0) {
            return GvApiResult.ETC_MODULE_RESET_FAILED;
        }
        //进入cpc卡3F00目录
        GvApiResult result = this.enterDir(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, "3F00");
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        //读取3F00目录下EF01系统信息文件
        String[] cosCmds = new String[1];
        cosCmds[0] = "00B081001E";
        result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmds);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        //解析系统信息文件
        CpcSysInfo cpcSysInfo = new CpcSysInfo();
        cpcSysInfo.setCpcInfo(cosCmds[0]);

        String defaultKey = "", newKey = "", testKey = "";
        if (area == 52) {
            defaultKey = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
            newKey = "00000000000000000000000000000000";
            testKey = "B629FC012B2F234E97A00972E530C752";
        } else {
            defaultKey = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
            newKey = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
            testKey = "B629FC012B2F234E97A00972E530C752";
        }

        Log.i(LOG_FLAG, "CPC Card provider: " + cpcSysInfo.getProvider());
        Log.i(LOG_FLAG, "CPC Card ID: " + cpcSysInfo.getCardId());
        String keyFlag = cpcSysInfo.getProvider().substring(14, 16);
        Log.i(LOG_FLAG, "分散: " + keyFlag);

        String inData = cpcSysInfo.getProvider().substring(0, 8)
                + cpcSysInfo.getProvider().substring(0, 8)
                + cpcSysInfo.getCardId();

        String dispersionKey;
        if ("01".equals(keyFlag)) {
            Log.i(LOG_FLAG, "二级分散");
            dispersionKey = sm4DivKey(testKey, 2, inData);
        } else if ("FE".equals(keyFlag)) {
            Log.i(LOG_FLAG, "一级分散");
            dispersionKey = sm4DivKey(testKey, 1, inData);
        } else {
            Log.i(LOG_FLAG, "不分散");
            dispersionKey = testKey;
        }
        if (TextUtils.isEmpty(dispersionKey)) {
            return GvApiResult.SM4_DIV_KEY_FAILED;
        }
        //进入DF01
        result = this.enterDir(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, "DF01");
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        //获取8字节随机数
        String[] random = new String[1];
        result = this.getChallenge(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, (byte) 8, random);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }

        random[0] += "0000000000000000";
        byte[] authData = new byte[8];
        //使用sm4加密随机数
        byte[] outDataByte = GvConvertUtils.hexStringToBytes(sm4Ecb(dispersionKey, 1, random[0]));
        //将加密后的16字节密文前后8字节进行异或
        for (int i = 0; i < 8; i++) {
            authData[i] = (byte) (outDataByte[i] ^ outDataByte[i + 8]);
        }
        //执行外部认证
        cosCmds[0] = "0082000108" + GvConvertUtils.bytesToHexString(authData, authData.length);
        Log.i(LOG_FLAG, "开始做外部认证");
        result = cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmds);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "外部认证失败");
            return result;
        }
        Log.i(LOG_FLAG, "外部认证返回指令：" + cosCmds[0]);
        //写出入口文件
        cosCmds[0] = "00D6810080" + cpcEntryExitInfo.getHexEF01Info();
        result = cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmds);
        if (result != GvApiResult.SUCCESS) {
            Log.e(LOG_FLAG, "写出口文件失败");
            return result;
        }
        //是出口，则返回
        if (cpcEntryExitInfo.getEntryExitStatusIntValue() == 2) {
            return result;
        }
        //是入口，则清除路径文件
        //清除过站信息
        result = this.clearCpcFile(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, "EF02");
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        //清除计费信息
        result = this.clearCpcFile(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, "EF04");
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        return result;
    }

    /**
     * 执行读CPC卡出入口信息
     * @param cpcEntryExitInfo CPC卡出入口信息
     * @return GvApiResult: 0成功，非0失败
     * @throws Exception
     */
    private GvApiResult doReadCpcEntryExitInfo(CpcEntryExitInfoMDL cpcEntryExitInfo, CPCEF02EF04InfoMDL cpcef02EF04InfoMDL) throws Exception {
        if ( cpcEntryExitInfo == null) {
            return GvApiResult.API_PARAMS_ERROR;
        }
        //复位cpc
        int ret = sdk.moduleReset((byte) 0);
        if (ret != 0) {
            return GvApiResult.ETC_MODULE_RESET_FAILED;
        }

        //进入cpc卡3F00目录
        GvApiResult result = this.enterDir(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, "3F00");
        if (result != GvApiResult.SUCCESS) {
            return result;
        }

        //读取系统文件MF/EF01（CPC卡）
        String[] cosCmdsoo1 = new String[1];
        cosCmdsoo1[0] = "00B081001E";
        result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmdsoo1);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        cpcSysInfo = new CpcSysInfoMDL();
        cpcSysInfo.setCpcInfo(cosCmdsoo1[0]);
        String mediaNo = cosCmdsoo1[0].substring(16,32);
        cpcef02EF04InfoMDL.setMediaNo(mediaNo);

        //读CPC基本信息文件MF/EF02（CPC卡）
        String[] cosCmdsMFEF02 = new String[1];
        cosCmdsMFEF02[0] = "00B0820040";
        result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmdsMFEF02);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        //设置EF02电量及5.8g开关信息
        cpcSysInfo.setBattry(cosCmdsMFEF02[0].substring(0, 2));
        cpcSysInfo.setIs58Open(cosCmdsMFEF02[0].substring(2, 4));
        //进入cpc卡DF01目录
        result = this.enterDir(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, "DF01");
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        //读CPC基本信息文件DF01/EF02（CPC卡）
        String[] cosCmdsoo = new String[1];
        cosCmdsoo[0] = "00B0820080";
        result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmdsoo);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
//        byte[] ef010 = GvConvertUtils.hexStringToBytes(cosCmdsoo[0].substring(0, cosCmdsoo[0].length() - 4));
        String ef02 = cosCmdsoo[0].substring(0, cosCmdsoo[0].length() - 4);

        cosCmdsoo[0] = "00B0828080";
        result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmdsoo);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
//        byte[] ef010 = GvConvertUtils.hexStringToBytes(cosCmdsoo[0].substring(0, cosCmdsoo[0].length() - 4));
        ef02 = ef02+cosCmdsoo[0].substring(0, cosCmdsoo[0].length() - 4);

        cosCmdsoo[0] = "00B0010080";
        result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmdsoo);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
//        byte[] ef010 = GvConvertUtils.hexStringToBytes(cosCmdsoo[0].substring(0, cosCmdsoo[0].length() - 4));
        ef02 = ef02+cosCmdsoo[0].substring(0, cosCmdsoo[0].length() - 4);

        cosCmdsoo[0] = "00B0018080";
        result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmdsoo);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
//        byte[] ef010 = GvConvertUtils.hexStringToBytes(cosCmdsoo[0].substring(0, cosCmdsoo[0].length() - 4));
        ef02 = ef02+cosCmdsoo[0].substring(0, cosCmdsoo[0].length() - 4);
        cpcef02EF04InfoMDL.setEF02(ef02);

        //ef04
        cosCmdsoo[0] = "00A4000002EF04";//选中EF04文件 后面读取时P1 P2为偏移量
        result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmdsoo);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        String[] rspData = new String[1];
        //80 转十进制128 一次最多读取128字节
        readPathFile(GvDevOptConstant.CHANNEL_TYPE_DSRC, 0, (byte)0x00, (byte)0x00, (byte)0x80, rspData);//0-128字节
        String ef04 = rspData[0];
        //从80（128）开始读取128字节
        readPathFile(GvDevOptConstant.CHANNEL_TYPE_DSRC, 0, (byte)0x00, (byte)0x80, (byte)0x80, rspData);//128-256字节
        ef04 = ef04+ rspData[0];
        //0100转十进制为256 表示从256字节开始读取 54（84字节 共340字节
//        readPathFile(GvDevOptConstant.CHANNEL_TYPE_DSRC, 0, (byte)0x01, (byte)0x00, (byte)0x54, rspData);//256+84=340字节
        readPathFile(GvDevOptConstant.CHANNEL_TYPE_DSRC, 0, (byte)0x01, (byte)0x00, (byte)0x80, rspData);//256+128=384字节
        ef04 = ef04+ rspData[0];
        //180 转十进制384 从384字节开始读取 7A(126)字节 共506字节 由于512最后6位保留 不用读取 只读506字节
        readPathFile(GvDevOptConstant.CHANNEL_TYPE_DSRC, 0, (byte)0x01, (byte)0x80, (byte)0x7A, rspData);//384+122=506字节
        ef04 = ef04+ rspData[0];
        cpcef02EF04InfoMDL.setEF04(ef04);//ef04 共512字节 后6字节保留 读取506字节

        //读取DF01目录下EF01文件
        String[] cosCmds = new String[1];
        cosCmds[0] = "00B0810080";
        result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmds);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }
        LogUtil.i("sdkhelper", "CPC卡出入口信息文件（ef01）:" + cosCmds[0]);
        byte[] ef01 = GvConvertUtils.hexStringToBytes(cosCmds[0].substring(0, cosCmds[0].length() - 4));
        cpcEntryExitInfo.setEntryExitInfo(ef01);
        return result;
    }

    /**
     * SM4二级分散密钥
     * @param key 初始密钥
     * @param divLevel 分散级别，1：一级分散，2：二级分散
     * @param inData 卡片ID
     * @return 二级分散后密钥
     */
    private String sm4DivKey(String key, int divLevel, String inData) {
        if (TextUtils.isEmpty(key)
                || TextUtils.isEmpty(inData)
                || inData.length() != 32
                || (divLevel != 1 && divLevel != 2)) {
            return "";
        }
        String divData = inData.substring(0, 16);
        byte[] divDataBytes;
        String dispersionKey;
        if (divLevel == 2) {
            divDataBytes = GvConvertUtils.hexStringToBytes(divData);
            for (int i = 0; i < 8; i++) {
                divDataBytes[i] = (byte) ~divDataBytes[i];
            }
            //B9F3D6DDB9F3D6DD 460C2922460C2922
            divData += GvConvertUtils.bytesToHexString(divDataBytes, divDataBytes.length);
            //4D896D27DAD48EBA2E87850891D0FA1E
            dispersionKey = sm4Ecb(key, 1, divData);
            Log.i(LOG_FLAG, "第一次分散得到：" + dispersionKey);
        } else {
            dispersionKey = key;
        }
        divData = inData.substring(16);
        divDataBytes = GvConvertUtils.hexStringToBytes(divData);
        for (int i = 0; i < 8; i++) {
            divDataBytes[i] = (byte) ~divDataBytes[i];
        }
        divData += GvConvertUtils.bytesToHexString(divDataBytes, divDataBytes.length);
        dispersionKey = sm4Ecb(dispersionKey, 1, divData);
        Log.i(LOG_FLAG, "第二次分散得到：" + dispersionKey);
        return dispersionKey;
    }

    /**
     * 加密或解密数据
     * @param key 旧密钥
     * @param mode 1:加密，0：解密
     * @param input 加密或解密前的数据
     * @return 加密或解密后的数据
     */
    private String sm4Ecb(String key, int mode, String input) {
        byte[] inputBytes = GvConvertUtils.hexStringToBytes(input);
        byte[] keyBytes = GvConvertUtils.hexStringToBytes(key);
        byte[] outputBytes = new byte[256];
        DESJNILib.Sm4Ecb(keyBytes, mode, inputBytes, inputBytes.length, outputBytes);
        return GvConvertUtils.bytesToHexString(outputBytes, inputBytes.length);
    }

    /**
     * 清除CPC卡信息文件（过站信息/计费信息）
     * @param channelType 通道类型
     * @param channelId 通道ID
     * @param fid 文件标识
     * @return GvApiResult : 0(成功) | 其他(失败)
     * @throws Exception
     */
    private GvApiResult clearCpcFile(int channelType, int channelId, String fid) throws Exception {
        Log.i(LOG_FLAG, "开始清除文件：" + fid);
        String[] cosCmds = new String[1];
        cosCmds[0] = "00A4000002" + fid;
        GvApiResult result = this.cosChannel(GvDevOptConstant.CHANNEL_TYPE_DSRC, GvDevOptConstant.DSRC_CHANNEL_ICC, 1, cosCmds);
        if (result != GvApiResult.SUCCESS) {
            return result;
        }

        byte[] fileBytes = new byte[128];
        String fileHexStr = GvConvertUtils.bytesToHexString(fileBytes, fileBytes.length);
        for (int i = 0; i < 4; i++) {
            cosCmds[0] = String.format("00D6%04X80%s", 128*i, fileHexStr);
            result = cosChannel(channelType, channelId, 1, cosCmds);
            if (result != GvApiResult.SUCCESS) {
                Log.e(LOG_FLAG, "清除文件失败!");
                return result;
            }
        }
        return result;
    }

    public void writeLog(String str){
        LogUtils.i(str+ "--------------------------------------------->\r\n");
//        writeLogInfo((str+ "--------------------------------------------->\r\n").getBytes());
    }

    /**
     * 写日志
     * @param content
     */
    /*public void writeLogInfo(byte[] content) {
        RandomAccessFile raf = null;
        try {
            //创建文件夹及文件
            File file = FileUtils.makeFilePath(fileBasePath, fileName);
            raf = new RandomAccessFile(file, "rwd");
            raf.seek(file.length());//跳转到文件末尾
            raf.write(content);//写入日志
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                raf.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }*/

}
